@github/copilot-linuxmusl-arm64
Advanced tools
Sorry, the diff of this file is too big to display
| --- | ||
| name: customize-cloud-agent | ||
| description: >- | ||
| Skill for customizing the Copilot cloud agent (formerly known as Copilot coding agent) environment, | ||
| including copilot-setup-steps.yml configuration, preinstalling tools and dependencies, runners, and settings. | ||
| Use when the user mentions copilot-setup-steps, copilot setup steps, or wants to configure the cloud agent environment. | ||
| user-invocable: false | ||
| --- | ||
| # Customizing the development environment for GitHub Copilot cloud agent | ||
| Learn how to customize GitHub Copilot's development environment with additional tools. | ||
| ## About customizing Copilot cloud agent's development environment | ||
| While working on a task, Copilot has access to its own ephemeral development environment, powered by GitHub Actions, where it can explore your code, make changes, execute automated tests and linters and more. | ||
| You can customize Copilot's development environment with a [Copilot setup steps file](#customizing-copilots-development-environment-with-copilot-setup-steps). You can use a Copilot setup steps file to: | ||
| - [Preinstall tools or dependencies in Copilot's environment](#preinstalling-tools-or-dependencies-in-copilots-environment) | ||
| - [Upgrade from standard GitHub-hosted GitHub Actions runners to larger runners](#upgrading-to-larger-github-hosted-github-actions-runners) | ||
| - [Run on GitHub Actions self-hosted runners](#using-self-hosted-github-actions-runners) | ||
| - [Give Copilot a Windows development environment](#switching-copilot-to-a-windows-development-environment), instead of the default Ubuntu Linux environment | ||
| - [Enable Git Large File Storage (LFS)](#enabling-git-large-file-storage-lfs) | ||
| In addition, you can: | ||
| - [Set environment variables in Copilot's environment](#setting-environment-variables-in-copilots-environment) | ||
| - [Disable or customize the agent's firewall](https://docs.github.com/enterprise-cloud@latest/copilot/customizing-copilot/customizing-or-disabling-the-firewall-for-copilot-coding-agent). | ||
| ## Customizing Copilot's development environment with Copilot setup steps | ||
| You can customize Copilot's environment by creating a special GitHub Actions workflow file, located at `.github/workflows/copilot-setup-steps.yml` within your repository. | ||
| A `copilot-setup-steps.yml` file looks like a normal GitHub Actions workflow file, but must contain a single `copilot-setup-steps` job. The steps in this job will be executed in GitHub Actions before Copilot starts working. For more information on GitHub Actions workflow files, see [Workflow syntax for GitHub Actions](https://docs.github.com/enterprise-cloud@latest/actions/using-workflows/workflow-syntax-for-github-actions). | ||
| > [!NOTE] | ||
| > The `copilot-setup-steps.yml` workflow won't trigger unless it's present on your default branch. | ||
| Here is a simple example of a `copilot-setup-steps.yml` file for a TypeScript project that clones the project, installs Node.js and downloads and caches the project's dependencies. You should customize this to fit your own project's language(s) and dependencies: | ||
| ```yaml | ||
| name: "Copilot Setup Steps" | ||
| # Automatically run the setup steps when they are changed to allow for easy validation, and | ||
| # allow manual testing through the repository's "Actions" tab | ||
| on: | ||
| workflow_dispatch: | ||
| push: | ||
| paths: | ||
| - .github/workflows/copilot-setup-steps.yml | ||
| pull_request: | ||
| paths: | ||
| - .github/workflows/copilot-setup-steps.yml | ||
| jobs: | ||
| # The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot. | ||
| copilot-setup-steps: | ||
| runs-on: ubuntu-latest | ||
| # Set the permissions to the lowest permissions possible needed for your steps. | ||
| # Copilot will be given its own token for its operations. | ||
| permissions: | ||
| # If you want to clone the repository as part of your setup steps, for example to install dependencies, you'll need the `contents: read` permission. | ||
| # If you don't clone the repository in your setup steps, Copilot will do this for you automatically after the steps complete. | ||
| contents: read | ||
| # You can define any steps you want, and they will run before the agent starts. | ||
| # If you do not check out your code, Copilot will do this for you. | ||
| steps: | ||
| # ... | ||
| ``` | ||
| In your `copilot-setup-steps.yml` file, you can only customize the following settings of the `copilot-setup-steps` job. If you try to customize other settings, your changes will be ignored. | ||
| - `steps` (see above) | ||
| - `permissions` (see above) | ||
| - `runs-on` (see below) | ||
| - `services` | ||
| - `snapshot` | ||
| - `timeout-minutes` (maximum value: `59`) | ||
| For more information on these options, see [Workflow syntax for GitHub Actions](https://docs.github.com/enterprise-cloud@latest/actions/writing-workflows/workflow-syntax-for-github-actions#jobs). | ||
| Any value that is set for the `fetch-depth` option of the `actions/checkout` action will be overridden to allow the agent to rollback commits upon request, while mitigating security risks. For more information, see [`actions/checkout/README.md`](https://github.com/actions/checkout/blob/main/README.md). | ||
| Your `copilot-setup-steps.yml` file will automatically be run as a normal GitHub Actions workflow when changes are made, so you can see if it runs successfully. This will show alongside other checks in a pull request where you create or modify the file. | ||
| Once you have merged the yml file into your default branch, you can manually run the workflow from the repository's **Actions** tab at any time to check that everything works as expected. For more information, see [Manually running a workflow](https://docs.github.com/enterprise-cloud@latest/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/manually-running-a-workflow). | ||
| When Copilot starts work, your setup steps will be run, and updates will show in the session logs. See [Tracking GitHub Copilot's sessions](https://docs.github.com/enterprise-cloud@latest/copilot/how-tos/agents/copilot-coding-agent/tracking-copilots-sessions). | ||
| If any setup step fails by returning a non-zero exit code, Copilot will skip the remaining setup steps and begin working with the current state of its development environment. | ||
| ## Preinstalling tools or dependencies in Copilot's environment | ||
| In its ephemeral development environment, Copilot can build or compile your project and run automated tests, linters and other tools. To do this, it will need to install your project's dependencies. | ||
| Copilot can discover and install these dependencies itself via a process of trial and error, but this can be slow and unreliable, given the non-deterministic nature of large language models (LLMs), and in some cases, it may be completely unable to download these dependencies—for example, if they are private. | ||
| You can use a Copilot setup steps file to deterministically install tools or dependencies before Copilot starts work. To do this, add `steps` to the `copilot-setup-steps` job: | ||
| ```yaml | ||
| # ... | ||
| jobs: | ||
| copilot-setup-steps: | ||
| # ... | ||
| # You can define any steps you want, and they will run before the agent starts. | ||
| # If you do not check out your code, Copilot will do this for you. | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v5 | ||
| - name: Set up Node.js | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: "20" | ||
| cache: "npm" | ||
| - name: Install JavaScript dependencies | ||
| run: npm ci | ||
| ``` | ||
| ## Upgrading to larger GitHub-hosted GitHub Actions runners | ||
| By default, Copilot works in a standard GitHub Actions runner. You can upgrade to larger runners for better performance (CPU and memory), more disk space and advanced features like Azure private networking. For more information, see [Larger runners](https://docs.github.com/enterprise-cloud@latest/actions/using-github-hosted-runners/using-larger-runners/about-larger-runners). | ||
| 1. Set up larger runners for your organization. For more information, see [Managing larger runners](https://docs.github.com/enterprise-cloud@latest/actions/using-github-hosted-runners/managing-larger-runners). | ||
| 2. If you are using larger runners with Azure private networking, configure your Azure private network to allow outbound access to the hosts required for Copilot cloud agent: | ||
| - `uploads.github.com` | ||
| - `user-images.githubusercontent.com` | ||
| - `api.individual.githubcopilot.com` (if you expect Copilot Pro or Copilot Pro+ users to use Copilot cloud agent in your repository) | ||
| - `api.business.githubcopilot.com` (if you expect Copilot Business users to use Copilot cloud agent in your repository) | ||
| - `api.enterprise.githubcopilot.com` (if you expect Copilot Enterprise users to use Copilot cloud agent in your repository) | ||
| - If you are using the OpenAI Codex third-party agent (for more information, see [About third-party agents](https://docs.github.com/enterprise-cloud@latest/copilot/concepts/agents/about-third-party-agents)): | ||
| - `npmjs.org` | ||
| - `npmjs.com` | ||
| - `registry.npmjs.com` | ||
| - `registry.npmjs.org` | ||
| - `skimdb.npmjs.com` | ||
| 3. Use a `copilot-setup-steps.yml` file in your repository to configure Copilot cloud agent to run on your chosen runners. Set the `runs-on` step of the `copilot-setup-steps` job to the label and/or group for the larger runners you want Copilot to use. For more information on specifying larger runners with `runs-on`, see [Running jobs on larger runners](https://docs.github.com/enterprise-cloud@latest/actions/using-github-hosted-runners/running-jobs-on-larger-runners). | ||
| ```yaml | ||
| # ... | ||
| jobs: | ||
| copilot-setup-steps: | ||
| runs-on: ubuntu-4-core | ||
| # ... | ||
| ``` | ||
| > [!NOTE] | ||
| > | ||
| > - Copilot cloud agent is only compatible with Ubuntu x64 Linux and Windows 64-bit runners. Runners with macOS or other operating systems are not supported. | ||
| ## Using self-hosted GitHub Actions runners | ||
| You can run Copilot cloud agent on self-hosted runners. You may want to do this to match how you run CI/CD workflows on GitHub Actions, or to give Copilot access to internal resources on your network. | ||
| We recommend that you only use Copilot cloud agent with ephemeral, single-use runners that are not reused for multiple jobs. Most customers set this up using ARC (Actions Runner Controller) or the GitHub Actions Runner Scale Set Client. For more information, see [Self-hosted runners reference](https://docs.github.com/enterprise-cloud@latest/actions/reference/runners/self-hosted-runners#supported-autoscaling-solutions). | ||
| > [!NOTE] | ||
| > Copilot cloud agent is only compatible with Ubuntu x64 and Windows 64-bit runners. Runners with macOS or other operating systems are not supported. | ||
| 1. Configure network security controls for your GitHub Actions runners to ensure that Copilot cloud agent does not have open access to your network or the public internet. | ||
| You must configure your firewall to allow connections to the [standard hosts required for GitHub Actions self-hosted runners](https://docs.github.com/enterprise-cloud@latest/actions/reference/runners/self-hosted-runners#accessible-domains-by-function), plus the following hosts: | ||
| - `uploads.github.com` | ||
| - `user-images.githubusercontent.com` | ||
| - `api.individual.githubcopilot.com` (if you expect Copilot Pro or Copilot Pro+ users to use Copilot cloud agent in your repository) | ||
| - `api.business.githubcopilot.com` (if you expect Copilot Business users to use Copilot cloud agent in your repository) | ||
| - `api.enterprise.githubcopilot.com` (if you expect Copilot Enterprise users to use Copilot cloud agent in your repository) | ||
| - If you are using the OpenAI Codex third-party agent (for more information, see [About third-party agents](https://docs.github.com/enterprise-cloud@latest/copilot/concepts/agents/about-third-party-agents)): | ||
| - `npmjs.org` | ||
| - `npmjs.com` | ||
| - `registry.npmjs.com` | ||
| - `registry.npmjs.org` | ||
| - `skimdb.npmjs.com` | ||
| 2. Disable Copilot cloud agent's integrated firewall in your repository settings. The firewall is not compatible with self-hosted runners. Unless this is disabled, use of Copilot cloud agent will be blocked. For more information, see [Customizing or disabling the firewall for GitHub Copilot cloud agent](https://docs.github.com/enterprise-cloud@latest/copilot/customizing-copilot/customizing-or-disabling-the-firewall-for-copilot-coding-agent). | ||
| 3. In your `copilot-setup-steps.yml` file, set the `runs-on` attribute to your ARC-managed scale set name: | ||
| ```yaml | ||
| # ... | ||
| jobs: | ||
| copilot-setup-steps: | ||
| runs-on: arc-scale-set-name | ||
| # ... | ||
| ``` | ||
| 4. If you want to configure a proxy server for Copilot cloud agent's connections to the internet, configure the following environment variables as appropriate: | ||
| | Variable | Description | Example | | ||
| | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | | ||
| | `https_proxy` | Proxy URL for HTTPS traffic. You can include basic authentication if required. | `http://proxy.local`<br>`http://192.168.1.1:8080`<br>`http://username:password@proxy.local` | | ||
| | `http_proxy` | Proxy URL for HTTP traffic. You can include basic authentication if required. | `http://proxy.local`<br>`http://192.168.1.1:8080`<br>`http://username:password@proxy.local` | | ||
| | `no_proxy` | A comma-separated list of hosts or IP addresses that should bypass the proxy. Some clients only honor IP addresses when connections are made directly to the IP rather than a hostname. | `example.com`<br>`example.com,myserver.local:443,example.org` | | ||
| | `ssl_cert_file` | The path to the SSL certificate presented by your proxy server. You will need to configure this if your proxy intercepts SSL connections. | `/path/to/key.pem` | | ||
| | `node_extra_ca_certs` | The path to the SSL certificate presented by your proxy server. You will need to configure this if your proxy intercepts SSL connections. | `/path/to/key.pem` | | ||
| You can set these environment variables by following the [instructions below](#setting-environment-variables-in-copilots-environment), or by setting them on the runner directly, for example with a custom runner image. For more information on building a custom image, see [Actions Runner Controller](https://docs.github.com/enterprise-cloud@latest/actions/concepts/runners/actions-runner-controller#creating-your-own-runner-image). | ||
| ## Switching Copilot to a Windows development environment | ||
| By default, Copilot uses an Ubuntu Linux-based development environment. | ||
| You may want to use a Windows development environment if you're building software for Windows or your repository uses a Windows-based toolchain so Copilot can build your project, run tests and validate its work. | ||
| Copilot cloud agent's integrated firewall is not compatible with Windows, so we recommend that you only use self-hosted runners or larger GitHub-hosted runners with Azure private networking where you can implement your own network controls. For more information on runners with Azure private networking, see [About Azure private networking for GitHub-hosted runners in your enterprise](https://docs.github.com/enterprise-cloud@latest/admin/configuring-settings/configuring-private-networking-for-hosted-compute-products/about-azure-private-networking-for-github-hosted-runners-in-your-enterprise). | ||
| To use Windows with self-hosted runners, follow the instructions in the [Using self-hosted GitHub Actions runners](#using-self-hosted-github-actions-runners) section above, using the label for your Windows runners. To use Windows with larger GitHub-hosted runners, follow the instructions in the [Upgrading to larger runners](#upgrading-to-larger-github-hosted-github-actions-runners) section above, using the label for your Windows runners. | ||
| ## Enabling Git Large File Storage (LFS) | ||
| If you use Git Large File Storage (LFS) to store large files in your repository, you will need to customize Copilot's environment to install Git LFS and fetch LFS objects. | ||
| To enable Git LFS, add a `actions/checkout` step to your `copilot-setup-steps` job with the `lfs` option set to `true`. | ||
| ```yaml | ||
| # ... | ||
| jobs: | ||
| copilot-setup-steps: | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read # for actions/checkout | ||
| steps: | ||
| - uses: actions/checkout@v5 | ||
| with: | ||
| lfs: true | ||
| ``` | ||
| ## Setting environment variables in Copilot's environment | ||
| You may want to set environment variables in Copilot's environment to configure or authenticate tools or dependencies that it has access to. | ||
| To set an environment variable for Copilot, create a GitHub Actions variable or secret in the `copilot` environment. If the value contains sensitive information, for example a password or API key, it's best to use a GitHub Actions secret. | ||
| 1. On GitHub, navigate to the main page of the repository. | ||
| 2. Under your repository name, click **Settings**. If you cannot see the "Settings" tab, select the **More** dropdown menu, then click **Settings**. | ||
| 3. In the left sidebar, click **Environments**. | ||
| 4. Click the `copilot` environment. | ||
| 5. To add a secret, under "Environment secrets," click **Add environment secret**. To add a variable, under "Environment variables," click **Add environment variable**. | ||
| 6. Fill in the "Name" and "Value" fields, and then click **Add secret** or **Add variable** as appropriate. | ||
| ## Further reading | ||
| - [Customizing or disabling the firewall for GitHub Copilot cloud agent](https://docs.github.com/enterprise-cloud@latest/copilot/customizing-copilot/customizing-or-disabling-the-firewall-for-copilot-coding-agent) |
| --- | ||
| name: customize-cloud-agent | ||
| description: >- | ||
| Skill for customizing the Copilot cloud agent (formerly known as Copilot coding agent) environment, | ||
| including copilot-setup-steps.yml configuration, preinstalling tools and dependencies, runners, and settings. | ||
| Use when the user mentions copilot-setup-steps, copilot setup steps, or wants to configure the cloud agent environment. | ||
| user-invocable: false | ||
| --- | ||
| # Customizing the development environment for GitHub Copilot cloud agent | ||
| Learn how to customize GitHub Copilot's development environment with additional tools. | ||
| ## About customizing Copilot cloud agent's development environment | ||
| While working on a task, Copilot has access to its own ephemeral development environment, powered by GitHub Actions, where it can explore your code, make changes, execute automated tests and linters and more. | ||
| You can customize Copilot's development environment with a [Copilot setup steps file](#customizing-copilots-development-environment-with-copilot-setup-steps). You can use a Copilot setup steps file to: | ||
| - [Preinstall tools or dependencies in Copilot's environment](#preinstalling-tools-or-dependencies-in-copilots-environment) | ||
| - [Upgrade from standard GitHub-hosted GitHub Actions runners to larger runners](#upgrading-to-larger-github-hosted-github-actions-runners) | ||
| - [Run on GitHub Actions self-hosted runners](#using-self-hosted-github-actions-runners) | ||
| - [Give Copilot a Windows development environment](#switching-copilot-to-a-windows-development-environment), instead of the default Ubuntu Linux environment | ||
| - [Enable Git Large File Storage (LFS)](#enabling-git-large-file-storage-lfs) | ||
| In addition, you can: | ||
| - [Set environment variables in Copilot's environment](#setting-environment-variables-in-copilots-environment) | ||
| - [Disable or customize the agent's firewall](https://docs.github.com/enterprise-cloud@latest/copilot/customizing-copilot/customizing-or-disabling-the-firewall-for-copilot-coding-agent). | ||
| ## Customizing Copilot's development environment with Copilot setup steps | ||
| You can customize Copilot's environment by creating a special GitHub Actions workflow file, located at `.github/workflows/copilot-setup-steps.yml` within your repository. | ||
| A `copilot-setup-steps.yml` file looks like a normal GitHub Actions workflow file, but must contain a single `copilot-setup-steps` job. The steps in this job will be executed in GitHub Actions before Copilot starts working. For more information on GitHub Actions workflow files, see [Workflow syntax for GitHub Actions](https://docs.github.com/enterprise-cloud@latest/actions/using-workflows/workflow-syntax-for-github-actions). | ||
| > [!NOTE] | ||
| > The `copilot-setup-steps.yml` workflow won't trigger unless it's present on your default branch. | ||
| Here is a simple example of a `copilot-setup-steps.yml` file for a TypeScript project that clones the project, installs Node.js and downloads and caches the project's dependencies. You should customize this to fit your own project's language(s) and dependencies: | ||
| ```yaml | ||
| name: "Copilot Setup Steps" | ||
| # Automatically run the setup steps when they are changed to allow for easy validation, and | ||
| # allow manual testing through the repository's "Actions" tab | ||
| on: | ||
| workflow_dispatch: | ||
| push: | ||
| paths: | ||
| - .github/workflows/copilot-setup-steps.yml | ||
| pull_request: | ||
| paths: | ||
| - .github/workflows/copilot-setup-steps.yml | ||
| jobs: | ||
| # The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot. | ||
| copilot-setup-steps: | ||
| runs-on: ubuntu-latest | ||
| # Set the permissions to the lowest permissions possible needed for your steps. | ||
| # Copilot will be given its own token for its operations. | ||
| permissions: | ||
| # If you want to clone the repository as part of your setup steps, for example to install dependencies, you'll need the `contents: read` permission. | ||
| # If you don't clone the repository in your setup steps, Copilot will do this for you automatically after the steps complete. | ||
| contents: read | ||
| # You can define any steps you want, and they will run before the agent starts. | ||
| # If you do not check out your code, Copilot will do this for you. | ||
| steps: | ||
| # ... | ||
| ``` | ||
| In your `copilot-setup-steps.yml` file, you can only customize the following settings of the `copilot-setup-steps` job. If you try to customize other settings, your changes will be ignored. | ||
| - `steps` (see above) | ||
| - `permissions` (see above) | ||
| - `runs-on` (see below) | ||
| - `services` | ||
| - `snapshot` | ||
| - `timeout-minutes` (maximum value: `59`) | ||
| For more information on these options, see [Workflow syntax for GitHub Actions](https://docs.github.com/enterprise-cloud@latest/actions/writing-workflows/workflow-syntax-for-github-actions#jobs). | ||
| Any value that is set for the `fetch-depth` option of the `actions/checkout` action will be overridden to allow the agent to rollback commits upon request, while mitigating security risks. For more information, see [`actions/checkout/README.md`](https://github.com/actions/checkout/blob/main/README.md). | ||
| Your `copilot-setup-steps.yml` file will automatically be run as a normal GitHub Actions workflow when changes are made, so you can see if it runs successfully. This will show alongside other checks in a pull request where you create or modify the file. | ||
| Once you have merged the yml file into your default branch, you can manually run the workflow from the repository's **Actions** tab at any time to check that everything works as expected. For more information, see [Manually running a workflow](https://docs.github.com/enterprise-cloud@latest/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/manually-running-a-workflow). | ||
| When Copilot starts work, your setup steps will be run, and updates will show in the session logs. See [Tracking GitHub Copilot's sessions](https://docs.github.com/enterprise-cloud@latest/copilot/how-tos/agents/copilot-coding-agent/tracking-copilots-sessions). | ||
| If any setup step fails by returning a non-zero exit code, Copilot will skip the remaining setup steps and begin working with the current state of its development environment. | ||
| ## Preinstalling tools or dependencies in Copilot's environment | ||
| In its ephemeral development environment, Copilot can build or compile your project and run automated tests, linters and other tools. To do this, it will need to install your project's dependencies. | ||
| Copilot can discover and install these dependencies itself via a process of trial and error, but this can be slow and unreliable, given the non-deterministic nature of large language models (LLMs), and in some cases, it may be completely unable to download these dependencies—for example, if they are private. | ||
| You can use a Copilot setup steps file to deterministically install tools or dependencies before Copilot starts work. To do this, add `steps` to the `copilot-setup-steps` job: | ||
| ```yaml | ||
| # ... | ||
| jobs: | ||
| copilot-setup-steps: | ||
| # ... | ||
| # You can define any steps you want, and they will run before the agent starts. | ||
| # If you do not check out your code, Copilot will do this for you. | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v5 | ||
| - name: Set up Node.js | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: "20" | ||
| cache: "npm" | ||
| - name: Install JavaScript dependencies | ||
| run: npm ci | ||
| ``` | ||
| ## Upgrading to larger GitHub-hosted GitHub Actions runners | ||
| By default, Copilot works in a standard GitHub Actions runner. You can upgrade to larger runners for better performance (CPU and memory), more disk space and advanced features like Azure private networking. For more information, see [Larger runners](https://docs.github.com/enterprise-cloud@latest/actions/using-github-hosted-runners/using-larger-runners/about-larger-runners). | ||
| 1. Set up larger runners for your organization. For more information, see [Managing larger runners](https://docs.github.com/enterprise-cloud@latest/actions/using-github-hosted-runners/managing-larger-runners). | ||
| 2. If you are using larger runners with Azure private networking, configure your Azure private network to allow outbound access to the hosts required for Copilot cloud agent: | ||
| - `uploads.github.com` | ||
| - `user-images.githubusercontent.com` | ||
| - `api.individual.githubcopilot.com` (if you expect Copilot Pro or Copilot Pro+ users to use Copilot cloud agent in your repository) | ||
| - `api.business.githubcopilot.com` (if you expect Copilot Business users to use Copilot cloud agent in your repository) | ||
| - `api.enterprise.githubcopilot.com` (if you expect Copilot Enterprise users to use Copilot cloud agent in your repository) | ||
| - If you are using the OpenAI Codex third-party agent (for more information, see [About third-party agents](https://docs.github.com/enterprise-cloud@latest/copilot/concepts/agents/about-third-party-agents)): | ||
| - `npmjs.org` | ||
| - `npmjs.com` | ||
| - `registry.npmjs.com` | ||
| - `registry.npmjs.org` | ||
| - `skimdb.npmjs.com` | ||
| 3. Use a `copilot-setup-steps.yml` file in your repository to configure Copilot cloud agent to run on your chosen runners. Set the `runs-on` step of the `copilot-setup-steps` job to the label and/or group for the larger runners you want Copilot to use. For more information on specifying larger runners with `runs-on`, see [Running jobs on larger runners](https://docs.github.com/enterprise-cloud@latest/actions/using-github-hosted-runners/running-jobs-on-larger-runners). | ||
| ```yaml | ||
| # ... | ||
| jobs: | ||
| copilot-setup-steps: | ||
| runs-on: ubuntu-4-core | ||
| # ... | ||
| ``` | ||
| > [!NOTE] | ||
| > | ||
| > - Copilot cloud agent is only compatible with Ubuntu x64 Linux and Windows 64-bit runners. Runners with macOS or other operating systems are not supported. | ||
| ## Using self-hosted GitHub Actions runners | ||
| You can run Copilot cloud agent on self-hosted runners. You may want to do this to match how you run CI/CD workflows on GitHub Actions, or to give Copilot access to internal resources on your network. | ||
| We recommend that you only use Copilot cloud agent with ephemeral, single-use runners that are not reused for multiple jobs. Most customers set this up using ARC (Actions Runner Controller) or the GitHub Actions Runner Scale Set Client. For more information, see [Self-hosted runners reference](https://docs.github.com/enterprise-cloud@latest/actions/reference/runners/self-hosted-runners#supported-autoscaling-solutions). | ||
| > [!NOTE] | ||
| > Copilot cloud agent is only compatible with Ubuntu x64 and Windows 64-bit runners. Runners with macOS or other operating systems are not supported. | ||
| 1. Configure network security controls for your GitHub Actions runners to ensure that Copilot cloud agent does not have open access to your network or the public internet. | ||
| You must configure your firewall to allow connections to the [standard hosts required for GitHub Actions self-hosted runners](https://docs.github.com/enterprise-cloud@latest/actions/reference/runners/self-hosted-runners#accessible-domains-by-function), plus the following hosts: | ||
| - `uploads.github.com` | ||
| - `user-images.githubusercontent.com` | ||
| - `api.individual.githubcopilot.com` (if you expect Copilot Pro or Copilot Pro+ users to use Copilot cloud agent in your repository) | ||
| - `api.business.githubcopilot.com` (if you expect Copilot Business users to use Copilot cloud agent in your repository) | ||
| - `api.enterprise.githubcopilot.com` (if you expect Copilot Enterprise users to use Copilot cloud agent in your repository) | ||
| - If you are using the OpenAI Codex third-party agent (for more information, see [About third-party agents](https://docs.github.com/enterprise-cloud@latest/copilot/concepts/agents/about-third-party-agents)): | ||
| - `npmjs.org` | ||
| - `npmjs.com` | ||
| - `registry.npmjs.com` | ||
| - `registry.npmjs.org` | ||
| - `skimdb.npmjs.com` | ||
| 2. Disable Copilot cloud agent's integrated firewall in your repository settings. The firewall is not compatible with self-hosted runners. Unless this is disabled, use of Copilot cloud agent will be blocked. For more information, see [Customizing or disabling the firewall for GitHub Copilot cloud agent](https://docs.github.com/enterprise-cloud@latest/copilot/customizing-copilot/customizing-or-disabling-the-firewall-for-copilot-coding-agent). | ||
| 3. In your `copilot-setup-steps.yml` file, set the `runs-on` attribute to your ARC-managed scale set name: | ||
| ```yaml | ||
| # ... | ||
| jobs: | ||
| copilot-setup-steps: | ||
| runs-on: arc-scale-set-name | ||
| # ... | ||
| ``` | ||
| 4. If you want to configure a proxy server for Copilot cloud agent's connections to the internet, configure the following environment variables as appropriate: | ||
| | Variable | Description | Example | | ||
| | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | | ||
| | `https_proxy` | Proxy URL for HTTPS traffic. You can include basic authentication if required. | `http://proxy.local`<br>`http://192.168.1.1:8080`<br>`http://username:password@proxy.local` | | ||
| | `http_proxy` | Proxy URL for HTTP traffic. You can include basic authentication if required. | `http://proxy.local`<br>`http://192.168.1.1:8080`<br>`http://username:password@proxy.local` | | ||
| | `no_proxy` | A comma-separated list of hosts or IP addresses that should bypass the proxy. Some clients only honor IP addresses when connections are made directly to the IP rather than a hostname. | `example.com`<br>`example.com,myserver.local:443,example.org` | | ||
| | `ssl_cert_file` | The path to the SSL certificate presented by your proxy server. You will need to configure this if your proxy intercepts SSL connections. | `/path/to/key.pem` | | ||
| | `node_extra_ca_certs` | The path to the SSL certificate presented by your proxy server. You will need to configure this if your proxy intercepts SSL connections. | `/path/to/key.pem` | | ||
| You can set these environment variables by following the [instructions below](#setting-environment-variables-in-copilots-environment), or by setting them on the runner directly, for example with a custom runner image. For more information on building a custom image, see [Actions Runner Controller](https://docs.github.com/enterprise-cloud@latest/actions/concepts/runners/actions-runner-controller#creating-your-own-runner-image). | ||
| ## Switching Copilot to a Windows development environment | ||
| By default, Copilot uses an Ubuntu Linux-based development environment. | ||
| You may want to use a Windows development environment if you're building software for Windows or your repository uses a Windows-based toolchain so Copilot can build your project, run tests and validate its work. | ||
| Copilot cloud agent's integrated firewall is not compatible with Windows, so we recommend that you only use self-hosted runners or larger GitHub-hosted runners with Azure private networking where you can implement your own network controls. For more information on runners with Azure private networking, see [About Azure private networking for GitHub-hosted runners in your enterprise](https://docs.github.com/enterprise-cloud@latest/admin/configuring-settings/configuring-private-networking-for-hosted-compute-products/about-azure-private-networking-for-github-hosted-runners-in-your-enterprise). | ||
| To use Windows with self-hosted runners, follow the instructions in the [Using self-hosted GitHub Actions runners](#using-self-hosted-github-actions-runners) section above, using the label for your Windows runners. To use Windows with larger GitHub-hosted runners, follow the instructions in the [Upgrading to larger runners](#upgrading-to-larger-github-hosted-github-actions-runners) section above, using the label for your Windows runners. | ||
| ## Enabling Git Large File Storage (LFS) | ||
| If you use Git Large File Storage (LFS) to store large files in your repository, you will need to customize Copilot's environment to install Git LFS and fetch LFS objects. | ||
| To enable Git LFS, add a `actions/checkout` step to your `copilot-setup-steps` job with the `lfs` option set to `true`. | ||
| ```yaml | ||
| # ... | ||
| jobs: | ||
| copilot-setup-steps: | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read # for actions/checkout | ||
| steps: | ||
| - uses: actions/checkout@v5 | ||
| with: | ||
| lfs: true | ||
| ``` | ||
| ## Setting environment variables in Copilot's environment | ||
| You may want to set environment variables in Copilot's environment to configure or authenticate tools or dependencies that it has access to. | ||
| To set an environment variable for Copilot, create a GitHub Actions variable or secret in the `copilot` environment. If the value contains sensitive information, for example a password or API key, it's best to use a GitHub Actions secret. | ||
| 1. On GitHub, navigate to the main page of the repository. | ||
| 2. Under your repository name, click **Settings**. If you cannot see the "Settings" tab, select the **More** dropdown menu, then click **Settings**. | ||
| 3. In the left sidebar, click **Environments**. | ||
| 4. Click the `copilot` environment. | ||
| 5. To add a secret, under "Environment secrets," click **Add environment secret**. To add a variable, under "Environment variables," click **Add environment variable**. | ||
| 6. Fill in the "Name" and "Value" fields, and then click **Add secret** or **Add variable** as appropriate. | ||
| ## Further reading | ||
| - [Customizing or disabling the firewall for GitHub Copilot cloud agent](https://docs.github.com/enterprise-cloud@latest/copilot/customizing-copilot/customizing-or-disabling-the-firewall-for-copilot-coding-agent) |
Sorry, the diff of this file is too big to display
| (()=>{const stack=new Error().stack;stack&&(globalThis._sentryDebugIds=globalThis._sentryDebugIds||{},globalThis._sentryDebugIds[stack]="7e888984-6fb9-5170-a11d-68d699424dce",globalThis._sentryDebugIdIdentifier="sentry-dbid-7e888984-6fb9-5170-a11d-68d699424dce");})(); | ||
| /*--------------------------------------------------------------------------------------------- | ||
| * Copyright (c) Microsoft Corporation. All rights reserved. | ||
| *--------------------------------------------------------------------------------------------*/ | ||
| import __module from "module"; | ||
| import __path from "path"; | ||
| import __fs from "fs"; | ||
| const __rootRequire = __module.createRequire(import.meta.url); | ||
| const __appPath = __fs.realpathSync(import.meta.dirname); | ||
| const __clipboardEntrypoint = __path.join(__appPath, "clipboard", "index.js"); | ||
| const __foundryEntrypoint = __path.join(__appPath, "foundry-local-sdk", "index.js"); | ||
| const __pvRecorderEntrypoint = __path.join(__appPath, "pvrecorder", "index.js"); | ||
| const __clipboardRequire = __fs.existsSync(__clipboardEntrypoint) | ||
| ? __module.createRequire(__clipboardEntrypoint) | ||
| : __rootRequire; | ||
| const __foundryRequire = __fs.existsSync(__foundryEntrypoint) | ||
| ? __module.createRequire(__foundryEntrypoint) | ||
| : __rootRequire; | ||
| const __pvRecorderRequire = __fs.existsSync(__pvRecorderEntrypoint) | ||
| ? __module.createRequire(__pvRecorderEntrypoint) | ||
| : __rootRequire; | ||
| const __isVendoredNativeModule = (module) => | ||
| typeof module === "string" && | ||
| (module.startsWith("@teddyzhu/") || module === "foundry-local-sdk" || module === "@picovoice/pvrecorder-node"); | ||
| const require = (module) => { | ||
| let req = __rootRequire; | ||
| if (typeof module === "string" && module.startsWith("@teddyzhu/")) { | ||
| req = __clipboardRequire; | ||
| } | ||
| if (module === "foundry-local-sdk") { | ||
| req = __foundryRequire; | ||
| } | ||
| if (module === "@picovoice/pvrecorder-node") { | ||
| req = __pvRecorderRequire; | ||
| } | ||
| if (typeof module === "string" && (__module.isBuiltin(module) || __isVendoredNativeModule(module))) { | ||
| return req(module); | ||
| } | ||
| const modulePath = __fs.realpathSync(req.resolve(module)); | ||
| const relativePath = __path.relative(__appPath, modulePath); | ||
| if (relativePath.startsWith("..")) { | ||
| throw new Error("Requiring module outside of application is a security concern; module: " + modulePath + ", app: " + __appPath); | ||
| } | ||
| return req(module); | ||
| };import __url from "url"; | ||
| const __filename = __url.fileURLToPath(import.meta.url); | ||
| const __dirname = __path.dirname(__filename); | ||
| var d=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(r,o)=>(typeof require<"u"?require:r)[o]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var v=(e,r)=>()=>(r||e((r={exports:{}}).exports,r),r.exports);var f=v(s=>{"use strict";Object.defineProperty(s,"__esModule",{value:!0});s.assign=m;s.loadNativeModule=y;function m(e){for(var r=[],o=1;o<arguments.length;o++)r[o-1]=arguments[o];return r.forEach(function(c){return Object.keys(c).forEach(function(t){return e[t]=c[t]})}),e}function y(e){for(var r=["build/Release","build/Debug","prebuilds/".concat(process.platform,"-").concat(process.arch)],o=["..","."],c,t=0,l=r;t<l.length;t++)for(var g=l[t],a=0,u=o;a<u.length;a++){var b=u[a],i="".concat(b,"/").concat(g);try{return{dir:i,module:d("".concat(i,"/").concat(e,".node"))}}catch(P){c=P}}throw new Error("Failed to load native module: ".concat(e,".node, checked: ").concat(r.join(", "),": ").concat(c))}});var E=v(p=>{Object.defineProperty(p,"__esModule",{value:!0});var M=f(),j=(0,M.loadNativeModule)("conpty_console_list").module.getConsoleProcessList,h=parseInt(process.argv[2],10),n=[];if(h>0)try{n=j(h)}catch{n=[]}process.send({consoleProcessList:n});process.exit(0)});export default E(); | ||
| //# sourceMappingURL=conpty_console_list_agent.js.map |
| /** | ||
| * Extension-owned canvases declared via | ||
| * `joinSession({ canvases: [createCanvas({...})] })`. | ||
| * | ||
| * The runtime sends provider callbacks directly as `canvas.open`, | ||
| * `canvas.close`, and `canvas.action.invoke` JSON-RPC requests. The SDK | ||
| * routes those requests by `canvasId` to the in-process handlers bound by | ||
| * `createCanvas`. Re-opening with an existing `instanceId` is how the host | ||
| * focuses an existing panel; reload is a renderer-only concern. | ||
| */ | ||
| /** JSON Schema object used for canvas inputs. */ | ||
| export type CanvasJsonSchema = Record<string, unknown>; | ||
| /** | ||
| * A single agent-callable action contributed by a canvas. The metadata | ||
| * (`name`, `description`, `inputSchema`) is serialized over the wire on | ||
| * `session.create` / `session.resume`; the `handler` closure is stripped | ||
| * before the declaration is sent and dispatched in-process by the SDK. | ||
| * | ||
| * Names MUST NOT start with `canvas.` — that prefix is reserved for | ||
| * lifecycle verbs. | ||
| */ | ||
| export interface CanvasAction { | ||
| /** Action identifier, unique within the canvas. */ | ||
| name: string; | ||
| /** Description shown to the model when picking an action. */ | ||
| description?: string; | ||
| /** Optional JSON Schema for the action's `input` payload. */ | ||
| inputSchema?: CanvasJsonSchema; | ||
| /** Required per-action dispatch handler. */ | ||
| handler: (ctx: CanvasActionContext) => Promise<unknown> | unknown; | ||
| } | ||
| /** | ||
| * Declarative metadata for a single canvas, serialized over the wire on | ||
| * `session.create` / `session.resume`. | ||
| */ | ||
| export interface CanvasDeclaration { | ||
| /** Canvas id, unique within the declaring connection. */ | ||
| id: string; | ||
| /** Human-readable label shown in discovery and host UI chrome. */ | ||
| displayName: string; | ||
| /** Short, single-sentence description shown to the agent in canvas catalogs. */ | ||
| description: string; | ||
| /** Optional JSON Schema for the `input` payload accepted by `canvas.open`. */ | ||
| inputSchema?: CanvasJsonSchema; | ||
| /** Agent-invocable actions exposed via `invoke_canvas_action`. */ | ||
| actions?: Omit<CanvasAction, "handler">[]; | ||
| } | ||
| /** Response returned from `open`. */ | ||
| export interface CanvasOpenResponse { | ||
| /** URL the host should render. Optional for native canvases. */ | ||
| url?: string; | ||
| /** Provider-supplied title shown in host chrome. */ | ||
| title?: string; | ||
| /** Provider-supplied status text shown in host chrome. */ | ||
| status?: string; | ||
| } | ||
| /** Host capabilities passed to canvas callbacks. */ | ||
| export interface CanvasHostContext { | ||
| capabilities?: { | ||
| canvases?: boolean; | ||
| }; | ||
| } | ||
| /** Context handed to a canvas's `open` handler. */ | ||
| export interface CanvasOpenContext { | ||
| /** Session that requested the canvas. */ | ||
| sessionId: string; | ||
| /** Extension id that owns the canvas. */ | ||
| extensionId: string; | ||
| /** Canvas id (matches the declaring `CanvasDeclaration.id`). */ | ||
| canvasId: string; | ||
| /** Stable instance id supplied by the runtime. */ | ||
| instanceId: string; | ||
| /** Validated `input` payload, shaped by `CanvasDeclaration.inputSchema`. */ | ||
| input: unknown; | ||
| /** Host capabilities supplied by the runtime. */ | ||
| host?: CanvasHostContext; | ||
| } | ||
| /** Context handed to a canvas action handler. */ | ||
| export interface CanvasActionContext { | ||
| /** Session that invoked the action. */ | ||
| sessionId: string; | ||
| /** Extension id that owns the canvas. */ | ||
| extensionId: string; | ||
| /** Canvas id targeted by the action. */ | ||
| canvasId: string; | ||
| /** Instance id targeted by the action. */ | ||
| instanceId: string; | ||
| /** Action name from `CanvasAction.name`. */ | ||
| actionName: string; | ||
| /** Validated `input` payload, shaped by the action's `inputSchema`. */ | ||
| input: unknown; | ||
| /** Host capabilities supplied by the runtime. */ | ||
| host?: CanvasHostContext; | ||
| } | ||
| /** Context handed to a canvas's `onClose` handler. */ | ||
| export interface CanvasLifecycleContext { | ||
| /** Session owning the canvas instance. */ | ||
| sessionId: string; | ||
| /** Extension id that owns the canvas. */ | ||
| extensionId: string; | ||
| /** Canvas id (matches the declaring `CanvasDeclaration.id`). */ | ||
| canvasId: string; | ||
| /** Instance id this lifecycle event applies to. */ | ||
| instanceId: string; | ||
| /** Host capabilities supplied by the runtime. */ | ||
| host?: CanvasHostContext; | ||
| } | ||
| /** Structured error returned from canvas handlers. */ | ||
| export declare class CanvasError extends Error { | ||
| readonly code: string; | ||
| constructor(code: string, message: string); | ||
| /** Default error when an action is declared but no `handler` is wired. */ | ||
| static noHandler(): CanvasError; | ||
| } | ||
| /** | ||
| * Options accepted by {@link createCanvas}. Combines the declarative | ||
| * {@link CanvasDeclaration} fields with the in-process handler closures. | ||
| */ | ||
| export interface CanvasOptions { | ||
| /** @see CanvasDeclaration.id */ | ||
| id: string; | ||
| /** @see CanvasDeclaration.displayName */ | ||
| displayName: string; | ||
| /** @see CanvasDeclaration.description */ | ||
| description: string; | ||
| /** @see CanvasDeclaration.inputSchema */ | ||
| inputSchema?: CanvasJsonSchema; | ||
| /** | ||
| * Agent-invocable actions exposed via `invoke_canvas_action`. Each action | ||
| * carries its own required `handler`; the action's wire metadata | ||
| * (`name`, `description`, `inputSchema`) is what reaches the runtime. | ||
| */ | ||
| actions?: CanvasAction[]; | ||
| /** Required. Open a new canvas instance. */ | ||
| open: (ctx: CanvasOpenContext) => Promise<CanvasOpenResponse> | CanvasOpenResponse; | ||
| /** | ||
| * Optional. Notified when a canvas instance is closed by the user, the | ||
| * agent, or the host. Fire-and-forget: the return value is ignored and | ||
| * errors are logged but not surfaced to the runtime. | ||
| */ | ||
| onClose?: (ctx: CanvasLifecycleContext) => Promise<void> | void; | ||
| } | ||
| /** A registered canvas: declarative metadata + in-process handler closures. | ||
| * | ||
| * Node intentionally uses a per-canvas factory pattern (mirroring | ||
| * {@link https://github.com/github/copilot-sdk | `DefineTool`}'s co-location | ||
| * ergonomics) where other SDKs (Rust, Python, Go, .NET) expose a single | ||
| * `CanvasHandler` per session that switches on `canvasId`. Both shapes target | ||
| * the same JSON-RPC wire protocol; the divergence is API ergonomics only. | ||
| */ | ||
| export declare class Canvas { | ||
| readonly declaration: CanvasDeclaration; | ||
| readonly open: NonNullable<CanvasOptions["open"]>; | ||
| readonly onClose?: CanvasOptions["onClose"]; | ||
| } | ||
| /** Create a canvas declaration with bound in-process handlers. | ||
| * | ||
| * Node intentionally uses this per-canvas factory pattern (mirroring | ||
| * `DefineTool`'s co-location ergonomics) where other SDKs (Rust, Python, Go, | ||
| * .NET) expose a single `CanvasHandler` per session that switches on | ||
| * `canvasId`. Both shapes target the same JSON-RPC wire protocol. | ||
| */ | ||
| export declare function createCanvas(options: CanvasOptions): Canvas; |
| import { createServerRpc } from "./generated/rpc.js"; | ||
| import { CopilotSession } from "./session.js"; | ||
| import type { CopilotClientOptions, GetAuthStatusResponse, GetStatusResponse, ModelInfo, ResumeSessionConfig, SessionConfig, SessionLifecycleEventType, SessionLifecycleHandler, SessionListFilter, SessionMetadata, TypedSessionLifecycleHandler } from "./types.js"; | ||
| /** | ||
| * Main client for interacting with the Copilot CLI. | ||
| * | ||
| * The CopilotClient manages the connection to the Copilot CLI server and provides | ||
| * methods to create and manage conversation sessions. It can either spawn a CLI | ||
| * server process or connect to an existing server. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { CopilotClient } from "@github/copilot-sdk"; | ||
| * | ||
| * // Create a client with default options (spawns CLI server) | ||
| * const client = new CopilotClient(); | ||
| * | ||
| * // Or connect to an existing server | ||
| * const client = new CopilotClient({ connection: RuntimeConnection.forUri("localhost:3000") }); | ||
| * | ||
| * // Create a session | ||
| * const session = await client.createSession({ onPermissionRequest: approveAll, model: "gpt-4" }); | ||
| * | ||
| * // Send messages and handle responses | ||
| * session.on((event) => { | ||
| * if (event.type === "assistant.message") { | ||
| * console.log(event.data.content); | ||
| * } | ||
| * }); | ||
| * await session.send({ prompt: "Hello!" }); | ||
| * | ||
| * // Clean up | ||
| * await session.disconnect(); | ||
| * await client.stop(); | ||
| * ``` | ||
| */ | ||
| export declare class CopilotClient { | ||
| private cliStartTimeout; | ||
| private cliProcess; | ||
| private connection; | ||
| private socket; | ||
| private runtimePort; | ||
| private actualHost; | ||
| private state; | ||
| private sessions; | ||
| private stderrBuffer; | ||
| /** Resolved connection mode chosen in the constructor. */ | ||
| private connectionConfig; | ||
| /** Resolved path to the runtime executable (only used for child-process kinds). */ | ||
| private resolvedCliPath; | ||
| /** Resolved environment passed to the spawned runtime. */ | ||
| private resolvedEnv; | ||
| private options; | ||
| private isExternalServer; | ||
| private forceStopping; | ||
| /** Token sent in `connect`; auto-generated when the SDK spawns its own CLI in TCP mode. */ | ||
| private effectiveConnectionToken?; | ||
| private onListModels?; | ||
| private onGetTraceContext?; | ||
| private modelsCache; | ||
| private modelsCacheLock; | ||
| private sessionLifecycleHandlers; | ||
| private typedLifecycleHandlers; | ||
| private _rpc; | ||
| private _internalRpc; | ||
| private processExitPromise; | ||
| private negotiatedProtocolVersion; | ||
| /** Connection-level session filesystem config, set via constructor option. */ | ||
| private sessionFsConfig; | ||
| /** | ||
| * Typed server-scoped RPC methods. | ||
| * @throws Error if the client is not connected | ||
| */ | ||
| get rpc(): ReturnType<typeof createServerRpc>; | ||
| /** | ||
| * Creates a new CopilotClient instance. | ||
| * | ||
| * @param options - Configuration options for the client | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * // Default: spawns the bundled runtime over stdio | ||
| * const client = new CopilotClient(); | ||
| * | ||
| * // Connect to an existing runtime | ||
| * const client = new CopilotClient({ | ||
| * connection: RuntimeConnection.forUri("localhost:3000"), | ||
| * }); | ||
| * | ||
| * // Spawn the runtime over TCP on a chosen port | ||
| * const client = new CopilotClient({ | ||
| * connection: RuntimeConnection.forTcp({ port: 9001 }), | ||
| * }); | ||
| * | ||
| * // Use a custom runtime binary | ||
| * const client = new CopilotClient({ | ||
| * connection: RuntimeConnection.forStdio({ path: "/usr/local/bin/copilot" }), | ||
| * logLevel: "debug", | ||
| * }); | ||
| * ``` | ||
| */ | ||
| constructor(options?: CopilotClientOptions); | ||
| private connectionExtraArgs; | ||
| /** | ||
| * Parse CLI URL into host and port | ||
| * Supports formats: "host:port", "http://host:port", "https://host:port", or just "port" | ||
| */ | ||
| private parseCliUrl; | ||
| private validateSessionFsConfig; | ||
| private setupSessionFs; | ||
| /** | ||
| * Starts the CLI server and establishes a connection. | ||
| * | ||
| * If connecting to an external server (via cliUrl), only establishes the connection. | ||
| * Otherwise, spawns the CLI server process and then connects. | ||
| * | ||
| * This method is called automatically the first time you create or resume a session. | ||
| * | ||
| * @returns A promise that resolves when the connection is established | ||
| * @throws Error if the server fails to start or the connection fails | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * const client = new CopilotClient(); | ||
| * await client.start(); | ||
| * // Now ready to create sessions | ||
| * ``` | ||
| */ | ||
| start(): Promise<void>; | ||
| /** | ||
| * Stops the CLI server and closes all active sessions. | ||
| * | ||
| * This method performs graceful cleanup: | ||
| * 1. Closes all active sessions (releases in-memory resources) | ||
| * 2. Closes the JSON-RPC connection | ||
| * 3. Terminates the CLI server process (if spawned by this client) | ||
| * | ||
| * Note: session data on disk is preserved, so sessions can be resumed later. | ||
| * To permanently remove session data before stopping, call | ||
| * {@link deleteSession} for each session first. | ||
| * | ||
| * @returns A promise that resolves with an array of errors encountered during cleanup. | ||
| * An empty array indicates all cleanup succeeded. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * const errors = await client.stop(); | ||
| * if (errors.length > 0) { | ||
| * console.error("Cleanup errors:", errors); | ||
| * } | ||
| * ``` | ||
| */ | ||
| stop(): Promise<Error[]>; | ||
| /** | ||
| * Alias for {@link stop} that lets `CopilotClient` participate in `await using` | ||
| * blocks for automatic cleanup. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * await using client = new CopilotClient(); | ||
| * const session = await client.createSession({ onPermissionRequest: approveAll }); | ||
| * await session.sendAndWait("Hello"); | ||
| * // client.stop() is called automatically when the block exits. | ||
| * ``` | ||
| */ | ||
| [Symbol.asyncDispose](): Promise<void>; | ||
| /** | ||
| * Forcefully stops the CLI server without graceful cleanup. | ||
| * | ||
| * Use this when {@link stop} fails or takes too long. This method: | ||
| * - Clears all sessions immediately without destroying them | ||
| * - Force closes the connection | ||
| * - Sends SIGKILL to the CLI process (if spawned by this client) | ||
| * | ||
| * @returns A promise that resolves when the force stop is complete | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * // If normal stop hangs, force stop | ||
| * const stopPromise = client.stop(); | ||
| * const timeout = new Promise((_, reject) => | ||
| * setTimeout(() => reject(new Error("Timeout")), 5000) | ||
| * ); | ||
| * | ||
| * try { | ||
| * await Promise.race([stopPromise, timeout]); | ||
| * } catch { | ||
| * await client.forceStop(); | ||
| * } | ||
| * ``` | ||
| */ | ||
| forceStop(): Promise<void>; | ||
| /** | ||
| * Creates a new conversation session with the Copilot CLI. | ||
| * | ||
| * Sessions maintain conversation state, handle events, and manage tool execution. | ||
| * If the client is not connected, this method automatically starts the connection. | ||
| * | ||
| * @param config - Optional configuration for the session | ||
| * @returns A promise that resolves with the created session | ||
| * @throws Error if the client fails to start | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * // Basic session | ||
| * const session = await client.createSession({ onPermissionRequest: approveAll }); | ||
| * | ||
| * // Session with model and tools | ||
| * const session = await client.createSession({ | ||
| * onPermissionRequest: approveAll, | ||
| * model: "gpt-4", | ||
| * tools: [{ | ||
| * name: "get_weather", | ||
| * description: "Get weather for a location", | ||
| * parameters: { type: "object", properties: { location: { type: "string" } } }, | ||
| * handler: async (args) => ({ temperature: 72 }) | ||
| * }] | ||
| * }); | ||
| * ``` | ||
| */ | ||
| createSession(config: SessionConfig): Promise<CopilotSession>; | ||
| /** | ||
| * Resumes an existing conversation session by its ID. | ||
| * | ||
| * This allows you to continue a previous conversation, maintaining all | ||
| * conversation history. The session must have been previously created | ||
| * and not deleted. | ||
| * | ||
| * @param sessionId - The ID of the session to resume | ||
| * @param config - Optional configuration for the resumed session | ||
| * @returns A promise that resolves with the resumed session | ||
| * @throws Error if the session does not exist or the client is not connected | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * // Resume a previous session | ||
| * const session = await client.resumeSession("session-123", { onPermissionRequest: approveAll }); | ||
| * | ||
| * // Resume with new tools | ||
| * const session = await client.resumeSession("session-123", { | ||
| * onPermissionRequest: approveAll, | ||
| * tools: [myNewTool] | ||
| * }); | ||
| * ``` | ||
| */ | ||
| resumeSession(sessionId: string, config: ResumeSessionConfig): Promise<CopilotSession>; | ||
| /** | ||
| * Sends a ping request to the server to verify connectivity. | ||
| * | ||
| * @param message - Optional message to include in the ping | ||
| * @returns A promise that resolves with the ping response containing the message and timestamp | ||
| * @throws Error if the client is not connected | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * const response = await client.ping("health check"); | ||
| * console.log(`Server responded at ${new Date(response.timestamp)}`); | ||
| * ``` | ||
| */ | ||
| ping(message?: string): Promise<{ | ||
| message: string; | ||
| timestamp: string; | ||
| protocolVersion?: number; | ||
| }>; | ||
| /** | ||
| * Get CLI status including version and protocol information | ||
| */ | ||
| getStatus(): Promise<GetStatusResponse>; | ||
| /** | ||
| * Get current authentication status | ||
| */ | ||
| getAuthStatus(): Promise<GetAuthStatusResponse>; | ||
| /** | ||
| * List available models with their metadata. | ||
| * | ||
| * If an `onListModels` handler was provided in the client options, | ||
| * it is called instead of querying the CLI server. | ||
| * | ||
| * Results are cached after the first successful call to avoid rate limiting. | ||
| * The cache is cleared when the client disconnects. | ||
| * | ||
| * @throws Error if not connected (when no custom handler is set) | ||
| */ | ||
| listModels(): Promise<ModelInfo[]>; | ||
| /** | ||
| * Send the `connect` handshake (carrying the optional token) and verify the | ||
| * server's protocol version. Falls back to `ping` against legacy servers | ||
| * that don't implement `connect`. | ||
| */ | ||
| private verifyProtocolVersion; | ||
| /** | ||
| * Gets the ID of the most recently updated session. | ||
| * | ||
| * This is useful for resuming the last conversation when the session ID | ||
| * was not stored. | ||
| * | ||
| * @returns A promise that resolves with the session ID, or undefined if no sessions exist | ||
| * @throws Error if the client is not connected | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * const lastId = await client.getLastSessionId(); | ||
| * if (lastId) { | ||
| * const session = await client.resumeSession(lastId, { onPermissionRequest: approveAll }); | ||
| * } | ||
| * ``` | ||
| */ | ||
| getLastSessionId(): Promise<string | undefined>; | ||
| /** | ||
| * Permanently deletes a session and all its data from disk, including | ||
| * conversation history, planning state, and artifacts. | ||
| * | ||
| * Unlike {@link CopilotSession.disconnect}, which only releases in-memory | ||
| * resources and preserves session data for later resumption, this method | ||
| * is irreversible. The session cannot be resumed after deletion. | ||
| * | ||
| * @param sessionId - The ID of the session to delete | ||
| * @returns A promise that resolves when the session is deleted | ||
| * @throws Error if the session does not exist or deletion fails | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * await client.deleteSession("session-123"); | ||
| * ``` | ||
| */ | ||
| deleteSession(sessionId: string): Promise<void>; | ||
| /** | ||
| * List all available sessions. | ||
| * | ||
| * @param filter - Optional filter to limit returned sessions by context fields | ||
| * | ||
| * @example | ||
| * // List all sessions | ||
| * const sessions = await client.listSessions(); | ||
| * | ||
| * @example | ||
| * // List sessions for a specific repository | ||
| * const sessions = await client.listSessions({ repository: "owner/repo" }); | ||
| */ | ||
| listSessions(filter?: SessionListFilter): Promise<SessionMetadata[]>; | ||
| /** | ||
| * Gets metadata for a specific session by ID. | ||
| * | ||
| * This provides an efficient O(1) lookup of a single session's metadata | ||
| * instead of listing all sessions. Returns undefined if the session is not found. | ||
| * | ||
| * @param sessionId - The ID of the session to look up | ||
| * @returns A promise that resolves with the session metadata, or undefined if not found | ||
| * @throws Error if the client is not connected | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * const metadata = await client.getSessionMetadata("session-123"); | ||
| * if (metadata) { | ||
| * console.log(`Session started at: ${metadata.startTime}`); | ||
| * } | ||
| * ``` | ||
| */ | ||
| getSessionMetadata(sessionId: string): Promise<SessionMetadata | undefined>; | ||
| private static toSessionMetadata; | ||
| /** | ||
| * Gets the foreground session ID in TUI+server mode. | ||
| * | ||
| * This returns the ID of the session currently displayed in the TUI. | ||
| * Only available when connecting to a server running in TUI+server mode (--ui-server). | ||
| * | ||
| * @returns A promise that resolves with the foreground session ID, or undefined if none | ||
| * @throws Error if the client is not connected | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * const sessionId = await client.getForegroundSessionId(); | ||
| * if (sessionId) { | ||
| * console.log(`TUI is displaying session: ${sessionId}`); | ||
| * } | ||
| * ``` | ||
| */ | ||
| getForegroundSessionId(): Promise<string | undefined>; | ||
| /** | ||
| * Sets the foreground session in TUI+server mode. | ||
| * | ||
| * This requests the TUI to switch to displaying the specified session. | ||
| * Only available when connecting to a server running in TUI+server mode (--ui-server). | ||
| * | ||
| * @param sessionId - The ID of the session to display in the TUI | ||
| * @returns A promise that resolves when the session is switched | ||
| * @throws Error if the client is not connected or if the operation fails | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * // Switch the TUI to display a specific session | ||
| * await client.setForegroundSessionId("session-123"); | ||
| * ``` | ||
| */ | ||
| setForegroundSessionId(sessionId: string): Promise<void>; | ||
| /** | ||
| * Subscribes to a specific session lifecycle event type. | ||
| * | ||
| * Lifecycle events are emitted when sessions are created, deleted, updated, | ||
| * or change foreground/background state (in TUI+server mode). | ||
| * | ||
| * @param eventType - The specific event type to listen for | ||
| * @param handler - A callback function that receives events of the specified type | ||
| * @returns A function that, when called, unsubscribes the handler | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * // Listen for when a session becomes foreground in TUI | ||
| * const unsubscribe = client.onLifecycle("session.foreground", (event) => { | ||
| * console.log(`Session ${event.sessionId} is now displayed in TUI`); | ||
| * }); | ||
| * | ||
| * // Later, to stop receiving events: | ||
| * unsubscribe(); | ||
| * ``` | ||
| */ | ||
| onLifecycle<K extends SessionLifecycleEventType>(eventType: K, handler: TypedSessionLifecycleHandler<K>): () => void; | ||
| /** | ||
| * Subscribes to all session lifecycle events. | ||
| * | ||
| * @param handler - A callback function that receives all lifecycle events | ||
| * @returns A function that, when called, unsubscribes the handler | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * const unsubscribe = client.onLifecycle((event) => { | ||
| * switch (event.type) { | ||
| * case "session.foreground": | ||
| * console.log(`Session ${event.sessionId} is now in foreground`); | ||
| * break; | ||
| * case "session.created": | ||
| * console.log(`New session created: ${event.sessionId}`); | ||
| * break; | ||
| * } | ||
| * }); | ||
| * | ||
| * // Later, to stop receiving events: | ||
| * unsubscribe(); | ||
| * ``` | ||
| */ | ||
| onLifecycle(handler: SessionLifecycleHandler): () => void; | ||
| /** | ||
| * Start the CLI server process | ||
| */ | ||
| private startCLIServer; | ||
| /** | ||
| * Connect to the CLI server (via socket or stdio) | ||
| */ | ||
| private connectToServer; | ||
| /** | ||
| * Connect to child via stdio pipes | ||
| */ | ||
| private connectToChildProcessViaStdio; | ||
| /** | ||
| * Connect to parent via stdio pipes | ||
| */ | ||
| private connectToParentProcessViaStdio; | ||
| /** | ||
| * Connect to the CLI server via TCP socket | ||
| */ | ||
| private connectViaTcp; | ||
| private attachConnectionHandlers; | ||
| private handleSessionEventNotification; | ||
| private handleSessionLifecycleNotification; | ||
| private handleUserInputRequest; | ||
| private handleExitPlanModeRequest; | ||
| private handleAutoModeSwitchRequest; | ||
| private handleHooksInvoke; | ||
| private handleSystemMessageTransform; | ||
| private handleCanvasProviderRequest; | ||
| private handleCanvasActionInvokeRequest; | ||
| } |
| # Agent Extension Authoring Guide | ||
| A precise, step-by-step reference for agents writing Copilot CLI extensions programmatically. | ||
| ## Workflow | ||
| ### Step 1: Scaffold the extension | ||
| Use the `extensions_manage` tool with `operation: "scaffold"`: | ||
| ``` | ||
| extensions_manage({ operation: "scaffold", name: "my-extension" }) | ||
| ``` | ||
| This creates `.github/extensions/my-extension/extension.mjs` with a working skeleton. | ||
| For user-scoped extensions (persist across all repos), add `location: "user"`. | ||
| ### Step 2: Edit the extension file | ||
| Modify the generated `extension.mjs` using `edit` or `create` tools. The file must: | ||
| - Be named `extension.mjs` (only `.mjs` is supported) | ||
| - Use ES module syntax (`import`/`export`) | ||
| - Call `joinSession({ ... })` | ||
| ### Step 3: Reload extensions | ||
| ``` | ||
| extensions_reload({}) | ||
| ``` | ||
| This stops all running extensions and re-discovers/re-launches them. New tools are available immediately in the same turn (mid-turn refresh). | ||
| ### Step 4: Verify | ||
| ``` | ||
| extensions_manage({ operation: "list" }) | ||
| extensions_manage({ operation: "inspect", name: "my-extension" }) | ||
| ``` | ||
| Check that the extension loaded successfully and isn't marked as "failed". | ||
| --- | ||
| ## File Structure | ||
| ``` | ||
| .github/extensions/<name>/extension.mjs | ||
| ``` | ||
| Discovery rules: | ||
| - The CLI scans `.github/extensions/` relative to the git root | ||
| - It also scans the user's copilot config extensions directory | ||
| - Only immediate subdirectories are checked (not recursive) | ||
| - Each subdirectory must contain a file named `extension.mjs` | ||
| - Project extensions shadow user extensions on name collision | ||
| --- | ||
| ## Minimal Skeleton | ||
| ```js | ||
| import { joinSession } from "@github/copilot-sdk/extension"; | ||
| await joinSession({ | ||
| tools: [], // Optional — custom tools | ||
| hooks: {}, // Optional — lifecycle hooks | ||
| }); | ||
| ``` | ||
| --- | ||
| ## Registering Tools | ||
| ```js | ||
| tools: [ | ||
| { | ||
| name: "tool_name", // Required. Must be globally unique across all extensions. | ||
| description: "What it does", // Required. Shown to the agent in tool descriptions. | ||
| parameters: { | ||
| // Optional. JSON Schema for the arguments. | ||
| type: "object", | ||
| properties: { | ||
| arg1: { type: "string", description: "..." }, | ||
| }, | ||
| required: ["arg1"], | ||
| }, | ||
| handler: async (args, invocation) => { | ||
| // args: parsed arguments matching the schema | ||
| // invocation.sessionId: current session ID | ||
| // invocation.toolCallId: unique call ID | ||
| // invocation.toolName: this tool's name | ||
| // | ||
| // Return value: string or ToolResultObject | ||
| // string → treated as success | ||
| // { textResultForLlm, resultType } → structured result | ||
| // resultType: "success" | "failure" | "rejected" | "denied" | ||
| return `Result: ${args.arg1}`; | ||
| }, | ||
| }, | ||
| ]; | ||
| ``` | ||
| **Constraints:** | ||
| - Tool names must be unique across ALL loaded extensions. Collisions cause the second extension to fail to load. | ||
| - Handler must return a string or `{ textResultForLlm: string, resultType?: string }`. | ||
| - Handler receives `(args, invocation)` — the second argument has `sessionId`, `toolCallId`, `toolName`. | ||
| - Use `session.log()` to surface messages to the user. Don't use `console.log()` (stdout is reserved for JSON-RPC). | ||
| --- | ||
| ## Registering Hooks | ||
| ```js | ||
| hooks: { | ||
| onUserPromptSubmitted: async (input, invocation) => { ... }, | ||
| onPreToolUse: async (input, invocation) => { ... }, | ||
| onPostToolUse: async (input, invocation) => { ... }, | ||
| onSessionStart: async (input, invocation) => { ... }, | ||
| onSessionEnd: async (input, invocation) => { ... }, | ||
| onErrorOccurred: async (input, invocation) => { ... }, | ||
| } | ||
| ``` | ||
| All hook inputs include `timestamp` (unix ms) and `cwd` (working directory). | ||
| All handlers receive `invocation: { sessionId: string }` as the second argument. | ||
| All handlers may return `void`/`undefined` (no-op) or an output object. | ||
| ### onUserPromptSubmitted | ||
| **Input:** `{ prompt: string, timestamp, cwd }` | ||
| **Output (all fields optional):** | ||
| | Field | Type | Effect | | ||
| |-------|------|--------| | ||
| | `modifiedPrompt` | `string` | Replaces the user's prompt | | ||
| | `additionalContext` | `string` | Appended as hidden context the agent sees | | ||
| ### onPreToolUse | ||
| **Input:** `{ toolName: string, toolArgs: unknown, timestamp, cwd }` | ||
| **Output (all fields optional):** | ||
| | Field | Type | Effect | | ||
| |-------|------|--------| | ||
| | `permissionDecision` | `"allow" \| "deny" \| "ask"` | Override the permission check | | ||
| | `permissionDecisionReason` | `string` | Shown to user if denied | | ||
| | `modifiedArgs` | `unknown` | Replaces the tool arguments | | ||
| | `additionalContext` | `string` | Injected into the conversation | | ||
| ### onPostToolUse | ||
| **Input:** `{ toolName: string, toolArgs: unknown, toolResult: ToolResultObject, timestamp, cwd }` | ||
| **Output (all fields optional):** | ||
| | Field | Type | Effect | | ||
| |-------|------|--------| | ||
| | `modifiedResult` | `ToolResultObject` | Replaces the tool result | | ||
| | `additionalContext` | `string` | Injected into the conversation | | ||
| ### onSessionStart | ||
| **Input:** `{ source: "startup" \| "resume" \| "new", initialPrompt?: string, timestamp, cwd }` | ||
| **Output (all fields optional):** | ||
| | Field | Type | Effect | | ||
| |-------|------|--------| | ||
| | `additionalContext` | `string` | Injected as initial context | | ||
| ### onSessionEnd | ||
| **Input:** `{ reason: "complete" \| "error" \| "abort" \| "timeout" \| "user_exit", finalMessage?: string, error?: string, timestamp, cwd }` | ||
| **Output (all fields optional):** | ||
| | Field | Type | Effect | | ||
| |-------|------|--------| | ||
| | `sessionSummary` | `string` | Summary for session persistence | | ||
| | `cleanupActions` | `string[]` | Cleanup descriptions | | ||
| ### onErrorOccurred | ||
| **Input:** `{ error: string, errorContext: "model_call" \| "tool_execution" \| "system" \| "user_input", recoverable: boolean, timestamp, cwd }` | ||
| **Output (all fields optional):** | ||
| | Field | Type | Effect | | ||
| |-------|------|--------| | ||
| | `errorHandling` | `"retry" \| "skip" \| "abort"` | How to handle the error | | ||
| | `retryCount` | `number` | Max retries (when errorHandling is "retry") | | ||
| | `userNotification` | `string` | Message shown to the user | | ||
| --- | ||
| ## Session Object | ||
| After `joinSession()`, the returned `session` provides: | ||
| ### session.send(options) | ||
| Send a message programmatically: | ||
| ```js | ||
| await session.send({ prompt: "Analyze the test results." }); | ||
| await session.send({ | ||
| prompt: "Review this file", | ||
| attachments: [{ type: "file", path: "./src/index.ts" }], | ||
| }); | ||
| ``` | ||
| ### session.sendAndWait(options, timeout?) | ||
| Send and block until the agent finishes (resolves on `session.idle`): | ||
| ```js | ||
| const response = await session.sendAndWait({ prompt: "What is 2+2?" }); | ||
| // response?.data.content contains the agent's reply | ||
| ``` | ||
| ### session.log(message, options?) | ||
| Log to the CLI timeline: | ||
| ```js | ||
| await session.log("Extension ready"); | ||
| await session.log("Rate limit approaching", { level: "warning" }); | ||
| await session.log("Connection failed", { level: "error" }); | ||
| await session.log("Processing...", { ephemeral: true }); // transient, not persisted | ||
| ``` | ||
| ### session.on(eventType, handler) | ||
| Subscribe to session events. Returns an unsubscribe function. | ||
| ```js | ||
| const unsub = session.on("tool.execution_complete", (event) => { | ||
| // event.data.toolName, event.data.success, event.data.result | ||
| }); | ||
| ``` | ||
| ### Key Event Types | ||
| | Event | Key Data Fields | | ||
| | ------------------------- | ------------------------------------------------------ | | ||
| | `assistant.message` | `content`, `messageId` | | ||
| | `tool.execution_start` | `toolCallId`, `toolName`, `arguments` | | ||
| | `tool.execution_complete` | `toolCallId`, `toolName`, `success`, `result`, `error` | | ||
| | `user.message` | `content`, `attachments`, `source` | | ||
| | `session.idle` | `backgroundTasks` | | ||
| | `session.error` | `errorType`, `message`, `stack` | | ||
| | `permission.requested` | `requestId`, `permissionRequest.kind` | | ||
| | `session.shutdown` | `shutdownType`, `totalPremiumRequests` | | ||
| ### session.workspacePath | ||
| Path to the session workspace directory (checkpoints, plan.md, files/). `undefined` if infinite sessions disabled. | ||
| ### session.rpc | ||
| Low-level typed RPC access to all session APIs (model, mode, plan, workspace, etc.). | ||
| --- | ||
| ## Gotchas | ||
| - **stdout is reserved for JSON-RPC.** Don't use `console.log()` — it will corrupt the protocol. Use `session.log()` to surface messages to the user. | ||
| - **Tool name collisions are fatal.** If two extensions register the same tool name, the second extension fails to initialize. | ||
| - **Don't call `session.send()` synchronously from `onUserPromptSubmitted`.** Use `setTimeout(() => session.send(...), 0)` to avoid infinite loops. | ||
| - **Extensions are reloaded on `/clear`.** Any in-memory state is lost between sessions. | ||
| - **Only `.mjs` is supported.** TypeScript (`.ts`) is not yet supported. | ||
| - **The handler's return value is the tool result.** Returning `undefined` sends an empty success. Throwing sends a failure with the error message. |
| # Copilot CLI Extension Examples | ||
| A practical guide to writing extensions using the `@github/copilot-sdk` extension API. | ||
| ## Extension Skeleton | ||
| Every extension starts with the same boilerplate: | ||
| ```js | ||
| import { joinSession } from "@github/copilot-sdk/extension"; | ||
| const session = await joinSession({ | ||
| hooks: { | ||
| /* ... */ | ||
| }, | ||
| tools: [ | ||
| /* ... */ | ||
| ], | ||
| }); | ||
| ``` | ||
| `joinSession` returns a `CopilotSession` object you can use to send messages and subscribe to events. | ||
| > **Platform notes (Windows vs macOS/Linux):** | ||
| > | ||
| > - Use `process.platform === "win32"` to detect Windows at runtime. | ||
| > - Clipboard: `pbcopy` on macOS, `clip` on Windows. | ||
| > - Use `exec()` instead of `execFile()` for `.cmd` scripts like `code`, `npx`, `npm` on Windows. | ||
| > - PowerShell stderr redirection uses `*>&1` instead of `2>&1`. | ||
| --- | ||
| ## Logging to the Timeline | ||
| Use `session.log()` to surface messages to the user in the CLI timeline: | ||
| ```js | ||
| const session = await joinSession({ | ||
| hooks: { | ||
| onSessionStart: async () => { | ||
| await session.log("My extension loaded"); | ||
| }, | ||
| onPreToolUse: async (input) => { | ||
| if (input.toolName === "bash") { | ||
| await session.log(`Running: ${input.toolArgs?.command}`, { ephemeral: true }); | ||
| } | ||
| }, | ||
| }, | ||
| tools: [], | ||
| }); | ||
| ``` | ||
| Levels: `"info"` (default), `"warning"`, `"error"`. Set `ephemeral: true` for transient messages that aren't persisted. | ||
| --- | ||
| ## Registering Custom Tools | ||
| Tools are functions the agent can call. Define them with a name, description, JSON Schema parameters, and a handler. | ||
| ### Basic tool | ||
| ```js | ||
| tools: [ | ||
| { | ||
| name: "my_tool", | ||
| description: "Does something useful", | ||
| parameters: { | ||
| type: "object", | ||
| properties: { | ||
| input: { type: "string", description: "The input value" }, | ||
| }, | ||
| required: ["input"], | ||
| }, | ||
| handler: async (args) => { | ||
| return `Processed: ${args.input}`; | ||
| }, | ||
| }, | ||
| ]; | ||
| ``` | ||
| ### Tool that invokes an external shell command | ||
| ```js | ||
| import { execFile } from "node:child_process"; | ||
| { | ||
| name: "run_command", | ||
| description: "Runs a shell command and returns its output", | ||
| parameters: { | ||
| type: "object", | ||
| properties: { | ||
| command: { type: "string", description: "The command to run" }, | ||
| }, | ||
| required: ["command"], | ||
| }, | ||
| handler: async (args) => { | ||
| const isWindows = process.platform === "win32"; | ||
| const shell = isWindows ? "powershell" : "bash"; | ||
| const shellArgs = isWindows | ||
| ? ["-NoProfile", "-Command", args.command] | ||
| : ["-c", args.command]; | ||
| return new Promise((resolve) => { | ||
| execFile(shell, shellArgs, (err, stdout, stderr) => { | ||
| if (err) resolve(`Error: ${stderr || err.message}`); | ||
| else resolve(stdout); | ||
| }); | ||
| }); | ||
| }, | ||
| } | ||
| ``` | ||
| ### Tool that calls an external API | ||
| ```js | ||
| { | ||
| name: "fetch_data", | ||
| description: "Fetches data from an API endpoint", | ||
| parameters: { | ||
| type: "object", | ||
| properties: { | ||
| url: { type: "string", description: "The URL to fetch" }, | ||
| }, | ||
| required: ["url"], | ||
| }, | ||
| handler: async (args) => { | ||
| const res = await fetch(args.url); | ||
| if (!res.ok) return `Error: HTTP ${res.status}`; | ||
| return await res.text(); | ||
| }, | ||
| } | ||
| ``` | ||
| ### Tool handler invocation context | ||
| The handler receives a second argument with invocation metadata: | ||
| ```js | ||
| handler: async (args, invocation) => { | ||
| // invocation.sessionId — current session ID | ||
| // invocation.toolCallId — unique ID for this tool call | ||
| // invocation.toolName — name of the tool being called | ||
| return "done"; | ||
| }; | ||
| ``` | ||
| --- | ||
| ## Hooks | ||
| Hooks intercept and modify behavior at key lifecycle points. Register them in the `hooks` option. | ||
| ### Available Hooks | ||
| | Hook | Fires When | Can Modify | | ||
| | ----------------------- | ------------------------- | ------------------------------------------- | | ||
| | `onUserPromptSubmitted` | User sends a message | The prompt text, add context | | ||
| | `onPreToolUse` | Before a tool executes | Tool args, permission decision, add context | | ||
| | `onPostToolUse` | After a tool executes | Tool result, add context | | ||
| | `onSessionStart` | Session starts or resumes | Add context, modify config | | ||
| | `onSessionEnd` | Session ends | Cleanup actions, summary | | ||
| | `onErrorOccurred` | An error occurs | Error handling strategy (retry/skip/abort) | | ||
| All hook inputs include `timestamp` (unix ms) and `cwd` (working directory). | ||
| ### Modifying the user's message | ||
| Use `onUserPromptSubmitted` to rewrite or augment what the user typed before the agent sees it. | ||
| ```js | ||
| hooks: { | ||
| onUserPromptSubmitted: async (input) => { | ||
| // Rewrite the prompt | ||
| return { modifiedPrompt: input.prompt.toUpperCase() }; | ||
| }, | ||
| } | ||
| ``` | ||
| ### Injecting additional context into every message | ||
| Return `additionalContext` to silently append instructions the agent will follow. | ||
| ```js | ||
| hooks: { | ||
| onUserPromptSubmitted: async (input) => { | ||
| return { | ||
| additionalContext: "Always respond in bullet points. Follow our team coding standards.", | ||
| }; | ||
| }, | ||
| } | ||
| ``` | ||
| ### Sending a follow-up message based on a keyword | ||
| Use `session.send()` to programmatically inject a new user message. | ||
| ```js | ||
| hooks: { | ||
| onUserPromptSubmitted: async (input) => { | ||
| if (/\\burgent\\b/i.test(input.prompt)) { | ||
| // Fire-and-forget a follow-up message | ||
| setTimeout(() => session.send({ prompt: "Please prioritize this." }), 0); | ||
| } | ||
| }, | ||
| } | ||
| ``` | ||
| > **Tip:** Guard against infinite loops if your follow-up message could re-trigger the same hook. | ||
| ### Blocking dangerous tool calls | ||
| Use `onPreToolUse` to inspect and optionally deny tool execution. | ||
| ```js | ||
| hooks: { | ||
| onPreToolUse: async (input) => { | ||
| if (input.toolName === "bash") { | ||
| const cmd = String(input.toolArgs?.command || ""); | ||
| if (/rm\\s+-rf/i.test(cmd) || /Remove-Item\\s+.*-Recurse/i.test(cmd)) { | ||
| return { | ||
| permissionDecision: "deny", | ||
| permissionDecisionReason: "Destructive commands are not allowed.", | ||
| }; | ||
| } | ||
| } | ||
| // Allow everything else | ||
| return { permissionDecision: "allow" }; | ||
| }, | ||
| } | ||
| ``` | ||
| ### Modifying tool arguments before execution | ||
| ```js | ||
| hooks: { | ||
| onPreToolUse: async (input) => { | ||
| if (input.toolName === "bash") { | ||
| const redirect = process.platform === "win32" ? "*>&1" : "2>&1"; | ||
| return { | ||
| modifiedArgs: { | ||
| ...input.toolArgs, | ||
| command: `${input.toolArgs.command} ${redirect}`, | ||
| }, | ||
| }; | ||
| } | ||
| }, | ||
| } | ||
| ``` | ||
| ### Reacting when the agent creates or edits a file | ||
| Use `onPostToolUse` to run side effects after a tool completes. | ||
| ```js | ||
| import { exec } from "node:child_process"; | ||
| hooks: { | ||
| onPostToolUse: async (input) => { | ||
| if (input.toolName === "create" || input.toolName === "edit") { | ||
| const filePath = input.toolArgs?.path; | ||
| if (filePath) { | ||
| // Open the file in VS Code | ||
| exec(`code "${filePath}"`, () => {}); | ||
| } | ||
| } | ||
| }, | ||
| } | ||
| ``` | ||
| ### Augmenting tool results with extra context | ||
| ```js | ||
| hooks: { | ||
| onPostToolUse: async (input) => { | ||
| if (input.toolName === "bash" && input.toolResult?.resultType === "failure") { | ||
| return { | ||
| additionalContext: "The command failed. Try a different approach.", | ||
| }; | ||
| } | ||
| }, | ||
| } | ||
| ``` | ||
| ### Running a linter after every file edit | ||
| ```js | ||
| import { exec } from "node:child_process"; | ||
| hooks: { | ||
| onPostToolUse: async (input) => { | ||
| if (input.toolName === "edit") { | ||
| const filePath = input.toolArgs?.path; | ||
| if (filePath?.endsWith(".ts")) { | ||
| const result = await new Promise((resolve) => { | ||
| exec(`npx eslint "${filePath}"`, (err, stdout) => { | ||
| resolve(err ? stdout : "No lint errors."); | ||
| }); | ||
| }); | ||
| return { additionalContext: `Lint result: ${result}` }; | ||
| } | ||
| } | ||
| }, | ||
| } | ||
| ``` | ||
| ### Handling errors with retry logic | ||
| ```js | ||
| hooks: { | ||
| onErrorOccurred: async (input) => { | ||
| if (input.recoverable && input.errorContext === "model_call") { | ||
| return { errorHandling: "retry", retryCount: 2 }; | ||
| } | ||
| return { | ||
| errorHandling: "abort", | ||
| userNotification: `An error occurred: ${input.error}`, | ||
| }; | ||
| }, | ||
| } | ||
| ``` | ||
| ### Session lifecycle hooks | ||
| ```js | ||
| hooks: { | ||
| onSessionStart: async (input) => { | ||
| // input.source is "startup", "resume", or "new" | ||
| return { additionalContext: "Remember to write tests for all changes." }; | ||
| }, | ||
| onSessionEnd: async (input) => { | ||
| // input.reason is "complete", "error", "abort", "timeout", or "user_exit" | ||
| }, | ||
| } | ||
| ``` | ||
| --- | ||
| ## Session Events | ||
| After calling `joinSession`, use `session.on()` to react to events in real time. | ||
| ### Listening to a specific event type | ||
| ```js | ||
| session.on("assistant.message", (event) => { | ||
| // event.data.content has the agent's response text | ||
| }); | ||
| ``` | ||
| ### Listening to all events | ||
| ```js | ||
| session.on((event) => { | ||
| // event.type and event.data are available for all events | ||
| }); | ||
| ``` | ||
| ### Unsubscribing from events | ||
| `session.on()` returns an unsubscribe function: | ||
| ```js | ||
| const unsubscribe = session.on("tool.execution_complete", (event) => { | ||
| // event.data.toolName, event.data.success, event.data.result, event.data.error | ||
| }); | ||
| // Later, stop listening | ||
| unsubscribe(); | ||
| ``` | ||
| ### Example: Auto-copy agent responses to clipboard | ||
| Combine a hook (to detect a keyword) with a session event (to capture the response): | ||
| ```js | ||
| import { execFile } from "node:child_process"; | ||
| let copyNextResponse = false; | ||
| function copyToClipboard(text) { | ||
| const cmd = process.platform === "win32" ? "clip" : "pbcopy"; | ||
| const proc = execFile(cmd, [], () => {}); | ||
| proc.stdin.write(text); | ||
| proc.stdin.end(); | ||
| } | ||
| const session = await joinSession({ | ||
| hooks: { | ||
| onUserPromptSubmitted: async (input) => { | ||
| if (/\\bcopy\\b/i.test(input.prompt)) { | ||
| copyNextResponse = true; | ||
| } | ||
| }, | ||
| }, | ||
| tools: [], | ||
| }); | ||
| session.on("assistant.message", (event) => { | ||
| if (copyNextResponse) { | ||
| copyNextResponse = false; | ||
| copyToClipboard(event.data.content); | ||
| } | ||
| }); | ||
| ``` | ||
| ### Top 10 Most Useful Event Types | ||
| | Event Type | Description | Key Data Fields | | ||
| | --------------------------- | ------------------------------------------------ | ------------------------------------------------------ | | ||
| | `assistant.message` | Agent's final response | `content`, `messageId`, `toolRequests` | | ||
| | `assistant.streaming_delta` | Token-by-token streaming (ephemeral) | `totalResponseSizeBytes` | | ||
| | `tool.execution_start` | A tool is about to run | `toolCallId`, `toolName`, `arguments` | | ||
| | `tool.execution_complete` | A tool finished running | `toolCallId`, `toolName`, `success`, `result`, `error` | | ||
| | `user.message` | User sent a message | `content`, `attachments`, `source` | | ||
| | `session.idle` | Session finished processing a turn | `backgroundTasks` | | ||
| | `session.error` | An error occurred | `errorType`, `message`, `stack` | | ||
| | `permission.requested` | Agent needs permission (shell, file write, etc.) | `requestId`, `permissionRequest.kind` | | ||
| | `session.shutdown` | Session is ending | `shutdownType`, `totalPremiumRequests`, `codeChanges` | | ||
| | `assistant.turn_start` | Agent begins a new thinking/response cycle | `turnId` | | ||
| ### Example: Detecting when the plan file is created or edited | ||
| Use `session.workspacePath` to locate the session's `plan.md`, then `fs.watchFile` to detect changes. | ||
| Correlate `tool.execution_start` / `tool.execution_complete` events by `toolCallId` to distinguish agent edits from user edits. | ||
| ```js | ||
| import { existsSync, watchFile, readFileSync } from "node:fs"; | ||
| import { join } from "node:path"; | ||
| import { joinSession } from "@github/copilot-sdk/extension"; | ||
| const agentEdits = new Set(); // toolCallIds for in-flight agent edits | ||
| const recentAgentPaths = new Set(); // paths recently written by the agent | ||
| const session = await joinSession(); | ||
| const workspace = session.workspacePath; // e.g. ~/.copilot/session-state/<id> | ||
| if (workspace) { | ||
| const planPath = join(workspace, "plan.md"); | ||
| let lastContent = existsSync(planPath) ? readFileSync(planPath, "utf-8") : null; | ||
| // Track agent edits to suppress false triggers | ||
| session.on("tool.execution_start", (event) => { | ||
| if ( | ||
| (event.data.toolName === "edit" || event.data.toolName === "create") && | ||
| String(event.data.arguments?.path || "").endsWith("plan.md") | ||
| ) { | ||
| agentEdits.add(event.data.toolCallId); | ||
| recentAgentPaths.add(planPath); | ||
| } | ||
| }); | ||
| session.on("tool.execution_complete", (event) => { | ||
| if (agentEdits.delete(event.data.toolCallId)) { | ||
| setTimeout(() => { | ||
| recentAgentPaths.delete(planPath); | ||
| lastContent = existsSync(planPath) ? readFileSync(planPath, "utf-8") : null; | ||
| }, 2000); | ||
| } | ||
| }); | ||
| watchFile(planPath, { interval: 1000 }, () => { | ||
| if (recentAgentPaths.has(planPath) || agentEdits.size > 0) return; | ||
| const content = existsSync(planPath) ? readFileSync(planPath, "utf-8") : null; | ||
| if (content === lastContent) return; | ||
| const wasCreated = lastContent === null && content !== null; | ||
| lastContent = content; | ||
| if (content !== null) { | ||
| session.send({ | ||
| prompt: `The plan was ${wasCreated ? "created" : "edited"} by the user.`, | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
| ``` | ||
| ### Example: Reacting when the user manually edits any file in the repo | ||
| Use `fs.watch` with `recursive: true` on `process.cwd()` to detect file changes. | ||
| Filter out agent edits by tracking `tool.execution_start` / `tool.execution_complete` events. | ||
| ```js | ||
| import { watch, readFileSync, statSync } from "node:fs"; | ||
| import { join, relative, resolve } from "node:path"; | ||
| import { joinSession } from "@github/copilot-sdk/extension"; | ||
| const agentEditPaths = new Set(); | ||
| const session = await joinSession(); | ||
| const cwd = process.cwd(); | ||
| const IGNORE = new Set(["node_modules", ".git", "dist"]); | ||
| // Track agent file edits | ||
| session.on("tool.execution_start", (event) => { | ||
| if (event.data.toolName === "edit" || event.data.toolName === "create") { | ||
| const p = String(event.data.arguments?.path || ""); | ||
| if (p) agentEditPaths.add(resolve(p)); | ||
| } | ||
| }); | ||
| session.on("tool.execution_complete", (event) => { | ||
| // Clear after a delay to avoid race with fs.watch | ||
| const p = [...agentEditPaths].find((x) => x); // any tracked path | ||
| setTimeout(() => agentEditPaths.clear(), 3000); | ||
| }); | ||
| const debounce = new Map(); | ||
| watch(cwd, { recursive: true }, (eventType, filename) => { | ||
| if (!filename || eventType !== "change") return; | ||
| if (filename.split(/[\\\\\\/]/).some((p) => IGNORE.has(p))) return; | ||
| if (debounce.has(filename)) clearTimeout(debounce.get(filename)); | ||
| debounce.set(filename, setTimeout(() => { | ||
| debounce.delete(filename); | ||
| const fullPath = join(cwd, filename); | ||
| if (agentEditPaths.has(resolve(fullPath))) return; | ||
| try { if (!statSync(fullPath).isFile()) return; } catch { return; } | ||
| const relPath = relative(cwd, fullPath); | ||
| session.send({ | ||
| prompt: `The user edited \\`${relPath}\\`.`, | ||
| attachments: [{ type: "file", path: fullPath }], | ||
| }); | ||
| }, 500)); | ||
| }); | ||
| ``` | ||
| --- | ||
| ## Sending Messages Programmatically | ||
| ### Fire-and-forget | ||
| ```js | ||
| await session.send({ prompt: "Analyze the test results." }); | ||
| ``` | ||
| ### Send and wait for the response | ||
| ```js | ||
| const response = await session.sendAndWait({ prompt: "What is 2 + 2?" }); | ||
| // response?.data.content contains the agent's reply | ||
| ``` | ||
| ### Send with file attachments | ||
| ```js | ||
| await session.send({ | ||
| prompt: "Review this file", | ||
| attachments: [{ type: "file", path: "./src/index.ts" }], | ||
| }); | ||
| ``` | ||
| --- | ||
| ## Permission and User Input Handlers | ||
| ### Custom permission logic | ||
| ```js | ||
| const session = await joinSession({ | ||
| onPermissionRequest: async (request) => { | ||
| if (request.kind === "shell") { | ||
| // request.fullCommandText has the shell command | ||
| return { kind: "approve-once" }; | ||
| } | ||
| if (request.kind === "write") { | ||
| return { kind: "approve-once" }; | ||
| } | ||
| return { kind: "reject" }; | ||
| }, | ||
| }); | ||
| ``` | ||
| ### Handling agent questions (ask_user) | ||
| Register `onUserInputRequest` to enable the agent's `ask_user` tool: | ||
| ```js | ||
| const session = await joinSession({ | ||
| onUserInputRequest: async (request) => { | ||
| // request.question has the agent's question | ||
| // request.choices has the options (if multiple choice) | ||
| return { answer: "yes", wasFreeform: false }; | ||
| }, | ||
| }); | ||
| ``` | ||
| --- | ||
| ## Complete Example: Multi-Feature Extension | ||
| An extension that combines tools, hooks, and events. | ||
| ```js | ||
| import { execFile, exec } from "node:child_process"; | ||
| import { joinSession } from "@github/copilot-sdk/extension"; | ||
| const isWindows = process.platform === "win32"; | ||
| let copyNextResponse = false; | ||
| function copyToClipboard(text) { | ||
| const proc = execFile(isWindows ? "clip" : "pbcopy", [], () => {}); | ||
| proc.stdin.write(text); | ||
| proc.stdin.end(); | ||
| } | ||
| function openInEditor(filePath) { | ||
| if (isWindows) exec(`code "${filePath}"`, () => {}); | ||
| else execFile("code", [filePath], () => {}); | ||
| } | ||
| const session = await joinSession({ | ||
| hooks: { | ||
| onUserPromptSubmitted: async (input) => { | ||
| if (/\\bcopy this\\b/i.test(input.prompt)) { | ||
| copyNextResponse = true; | ||
| } | ||
| return { | ||
| additionalContext: "Follow our team style guide. Use 4-space indentation.", | ||
| }; | ||
| }, | ||
| onPreToolUse: async (input) => { | ||
| if (input.toolName === "bash") { | ||
| const cmd = String(input.toolArgs?.command || ""); | ||
| if (/rm\\s+-rf\\s+\\/ / i.test(cmd) || /Remove-Item\\s+.*-Recurse/i.test(cmd)) { | ||
| return { permissionDecision: "deny" }; | ||
| } | ||
| } | ||
| }, | ||
| onPostToolUse: async (input) => { | ||
| if (input.toolName === "create" || input.toolName === "edit") { | ||
| const filePath = input.toolArgs?.path; | ||
| if (filePath) openInEditor(filePath); | ||
| } | ||
| }, | ||
| }, | ||
| tools: [ | ||
| { | ||
| name: "copy_to_clipboard", | ||
| description: "Copies text to the system clipboard.", | ||
| parameters: { | ||
| type: "object", | ||
| properties: { | ||
| text: { type: "string", description: "Text to copy" }, | ||
| }, | ||
| required: ["text"], | ||
| }, | ||
| handler: async (args) => { | ||
| return new Promise((resolve) => { | ||
| const proc = execFile(isWindows ? "clip" : "pbcopy", [], (err) => { | ||
| if (err) resolve(`Error: ${err.message}`); | ||
| else resolve("Copied to clipboard."); | ||
| }); | ||
| proc.stdin.write(args.text); | ||
| proc.stdin.end(); | ||
| }); | ||
| }, | ||
| }, | ||
| ], | ||
| }); | ||
| session.on("assistant.message", (event) => { | ||
| if (copyNextResponse) { | ||
| copyNextResponse = false; | ||
| copyToClipboard(event.data.content); | ||
| } | ||
| }); | ||
| session.on("tool.execution_complete", (event) => { | ||
| // event.data.success, event.data.toolName, event.data.result | ||
| }); | ||
| ``` |
| # Copilot CLI Extensions | ||
| Extensions add custom tools, hooks, and behaviors to the Copilot CLI. They run as separate Node.js processes that communicate with the CLI over JSON-RPC via stdio. | ||
| ## How Extensions Work | ||
| ``` | ||
| ┌─────────────────────┐ JSON-RPC / stdio ┌──────────────────────┐ | ||
| │ Copilot CLI │ ◄──────────────────────────────────► │ Extension Process │ | ||
| │ (parent process) │ tool calls, events, hooks │ (forked child) │ | ||
| │ │ │ │ | ||
| │ • Discovers exts │ │ • Registers tools │ | ||
| │ • Forks processes │ │ • Registers hooks │ | ||
| │ • Routes tool calls │ │ • Listens to events │ | ||
| │ • Manages lifecycle │ │ • Uses SDK APIs │ | ||
| └─────────────────────┘ └──────────────────────┘ | ||
| ``` | ||
| 1. **Discovery**: The CLI scans `.github/extensions/` (project) and the user's copilot config extensions directory for subdirectories containing `extension.mjs`. | ||
| 2. **Launch**: Each extension is forked as a child process with `@github/copilot-sdk` available via an automatic module resolver. | ||
| 3. **Connection**: The extension calls `joinSession()` which establishes a JSON-RPC connection over stdio to the CLI and attaches to the user's current foreground session. | ||
| 4. **Registration**: Tools and hooks declared in the session options are registered with the CLI and become available to the agent. | ||
| 5. **Lifecycle**: Extensions are reloaded on `/clear` (or if the foreground session is replaced) and stopped on CLI exit (SIGTERM, then SIGKILL after 5s). | ||
| ## File Structure | ||
| ``` | ||
| .github/extensions/ | ||
| my-extension/ | ||
| extension.mjs ← Entry point (required, must be .mjs) | ||
| ``` | ||
| - Only `.mjs` files are supported (ES modules). The file must be named `extension.mjs`. | ||
| - Each extension lives in its own subdirectory. | ||
| - The `@github/copilot-sdk` import is resolved automatically — you don't install it. | ||
| ## The SDK | ||
| Extensions use `@github/copilot-sdk` for all interactions with the CLI: | ||
| ```js | ||
| import { joinSession } from "@github/copilot-sdk/extension"; | ||
| const session = await joinSession({ | ||
| tools: [ | ||
| /* ... */ | ||
| ], | ||
| hooks: { | ||
| /* ... */ | ||
| }, | ||
| }); | ||
| ``` | ||
| The `session` object provides methods for sending messages, logging to the timeline, listening to events, and accessing the RPC API. See the `.d.ts` files in the SDK package for full type information. | ||
| ## Further Reading | ||
| - `examples.md` — Practical code examples for tools, hooks, events, and complete extensions | ||
| - `agent-author.md` — Step-by-step workflow for agents authoring extensions programmatically |
| import type { CopilotSession } from "./session.js"; | ||
| import { type ExtensionInfo, type PermissionHandler, type ResumeSessionConfig } from "./types.js"; | ||
| export { Canvas, CanvasError, createCanvas, type CanvasAction, type CanvasActionContext, type CanvasDeclaration, type CanvasHostContext, type CanvasJsonSchema, type CanvasLifecycleContext, type CanvasOpenContext, type CanvasOpenResponse, type CanvasOptions, } from "./canvas.js"; | ||
| export type JoinSessionConfig = Omit<ResumeSessionConfig, "onPermissionRequest"> & { | ||
| onPermissionRequest?: PermissionHandler; | ||
| }; | ||
| export type { ExtensionInfo }; | ||
| /** | ||
| * Joins the current foreground session. | ||
| * | ||
| * @param config - Configuration to add to the session | ||
| * @returns A promise that resolves with the joined session | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { joinSession } from "@github/copilot-sdk/extension"; | ||
| * | ||
| * const session = await joinSession({ tools: [myTool] }); | ||
| * ``` | ||
| */ | ||
| export declare function joinSession(config?: JoinSessionConfig): Promise<CopilotSession>; |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
| /** | ||
| * Copilot SDK - TypeScript/Node.js Client | ||
| * | ||
| * JSON-RPC based SDK for programmatic control of GitHub Copilot CLI | ||
| */ | ||
| export { CopilotClient } from "./client.js"; | ||
| export { RuntimeConnection } from "./types.js"; | ||
| export { CopilotSession, type AssistantMessageEvent } from "./session.js"; | ||
| export { Canvas, CanvasError, createCanvas, type CanvasAction, type CanvasActionContext, type CanvasDeclaration, type CanvasHostContext, type CanvasJsonSchema, type CanvasLifecycleContext, type CanvasOpenContext, type CanvasOpenResponse, type CanvasOptions, } from "./canvas.js"; | ||
| export { defineTool, approveAll, convertMcpCallToolResult, createSessionFsAdapter, SYSTEM_MESSAGE_SECTIONS, } from "./types.js"; | ||
| export type * from "./generated/session-events.js"; | ||
| export type { CommandContext, CommandDefinition, CommandHandler, CloudSessionOptions, CloudSessionRepository, AutoModeSwitchHandler, AutoModeSwitchRequest, AutoModeSwitchResponse, CopilotClientOptions, StdioRuntimeConnection, TcpRuntimeConnection, UriRuntimeConnection, CustomAgentConfig, ElicitationFieldValue, ElicitationHandler, ElicitationParams, ElicitationContext, ElicitationResult, ElicitationSchema, ElicitationSchemaField, ExitPlanModeHandler, ExitPlanModeRequest, ExitPlanModeResult, ExtensionInfo, ForegroundSessionInfo, GetAuthStatusResponse, GetStatusResponse, InfiniteSessionConfig, UiInputOptions, MCPStdioServerConfig, MCPHTTPServerConfig, MCPServerConfig, DefaultAgentConfig, MessageOptions, ModelBilling, ModelCapabilities, ModelCapabilitiesOverride, ModelInfo, ModelPolicy, PermissionHandler, PermissionRequest, PermissionRequestResult, ProviderConfig, RemoteSessionMode, ResumeSessionConfig, SectionOverride, SectionOverrideAction, SectionTransformFn, SessionCapabilities, SessionConfig, SessionConfigBase, SessionEvent, SessionEventHandler, SessionEventPayload, SessionEventType, SessionLifecycleEvent, SessionLifecycleEventMetadata, SessionLifecycleEventType, SessionLifecycleHandler, SessionCreatedEvent, SessionDeletedEvent, SessionUpdatedEvent, SessionForegroundEvent, SessionBackgroundEvent, SessionContext, SessionListFilter, SessionMetadata, SessionUiApi, SessionFsConfig, SessionFsProvider, SessionFsFileInfo, SessionFsSqliteQueryResult, SessionFsSqliteQueryType, SessionFsSqliteProvider, SystemMessageAppendConfig, SystemMessageConfig, SystemMessageCustomizeConfig, SystemMessageReplaceConfig, SystemMessageSection, TelemetryConfig, TraceContext, TraceContextProvider, Tool, ToolHandler, ToolInvocation, ToolTelemetry, ToolResultObject, TypedSessionEventHandler, TypedSessionLifecycleHandler, ZodSchema, } from "./types.js"; |
Sorry, the diff of this file is too big to display
| /** | ||
| * The SDK protocol version. | ||
| * This must match the version expected by the copilot-agent-runtime server. | ||
| */ | ||
| export declare const SDK_PROTOCOL_VERSION = 3; | ||
| /** | ||
| * Gets the SDK protocol version. | ||
| * @returns The protocol version number | ||
| */ | ||
| export declare function getSdkProtocolVersion(): number; |
| import { createSessionRpc } from "./generated/rpc.js"; | ||
| import type { OpenCanvasInstance } from "./generated/rpc.js"; | ||
| import type { MessageOptions, ReasoningEffort, ModelCapabilitiesOverride, SessionCapabilities, SessionEvent, SessionEventHandler, SessionEventType, SessionUiApi, TypedSessionEventHandler } from "./types.js"; | ||
| /** Assistant message event - the final response from the assistant. */ | ||
| export type AssistantMessageEvent = Extract<SessionEvent, { | ||
| type: "assistant.message"; | ||
| }>; | ||
| /** | ||
| * Represents a single conversation session with the Copilot CLI. | ||
| * | ||
| * A session maintains conversation state, handles events, and manages tool execution. | ||
| * Sessions are created via {@link CopilotClient.createSession} or resumed via | ||
| * {@link CopilotClient.resumeSession}. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * const session = await client.createSession({ model: "gpt-4" }); | ||
| * | ||
| * // Subscribe to events | ||
| * session.on((event) => { | ||
| * if (event.type === "assistant.message") { | ||
| * console.log(event.data.content); | ||
| * } | ||
| * }); | ||
| * | ||
| * // Send a message and wait for completion | ||
| * await session.sendAndWait({ prompt: "Hello, world!" }); | ||
| * | ||
| * // Clean up | ||
| * await session.disconnect(); | ||
| * ``` | ||
| */ | ||
| export declare class CopilotSession { | ||
| readonly sessionId: string; | ||
| private connection; | ||
| private _workspacePath?; | ||
| private eventHandlers; | ||
| private typedEventHandlers; | ||
| private toolHandlers; | ||
| private canvases; | ||
| private commandHandlers; | ||
| private permissionHandler?; | ||
| private userInputHandler?; | ||
| private elicitationHandler?; | ||
| private exitPlanModeHandler?; | ||
| private autoModeSwitchHandler?; | ||
| private hooks?; | ||
| private transformCallbacks?; | ||
| private _rpc; | ||
| private traceContextProvider?; | ||
| private _capabilities; | ||
| private openCanvasInstances; | ||
| /** | ||
| * Typed session-scoped RPC methods. | ||
| */ | ||
| get rpc(): ReturnType<typeof createSessionRpc>; | ||
| /** | ||
| * Path to the session workspace directory when infinite sessions are enabled. | ||
| * Contains checkpoints/, plan.md, and files/ subdirectories. | ||
| * Undefined if infinite sessions are disabled. | ||
| */ | ||
| get workspacePath(): string | undefined; | ||
| /** | ||
| * Host capabilities reported when the session was created or resumed. | ||
| * Use this to check feature support before calling capability-gated APIs. | ||
| */ | ||
| get capabilities(): SessionCapabilities; | ||
| /** | ||
| * Interactive UI methods for showing dialogs to the user. | ||
| * Only available when the CLI host supports elicitation | ||
| * (`session.capabilities.ui?.elicitation === true`). | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * if (session.capabilities.ui?.elicitation) { | ||
| * const ok = await session.ui.confirm("Deploy to production?"); | ||
| * } | ||
| * ``` | ||
| */ | ||
| get ui(): SessionUiApi; | ||
| /** | ||
| * Sends a message to this session and waits for the response. | ||
| * | ||
| * The message is processed asynchronously. Subscribe to events via {@link on} | ||
| * to receive streaming responses and other session events. | ||
| * | ||
| * @param options - The message options including the prompt and optional attachments | ||
| * @returns A promise that resolves with the message ID of the response | ||
| * @throws Error if the session has been disconnected or the connection fails | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * const messageId = await session.send({ | ||
| * prompt: "Explain this code", | ||
| * attachments: [{ type: "file", path: "./src/index.ts" }] | ||
| * }); | ||
| * ``` | ||
| */ | ||
| send(prompt: string): Promise<string>; | ||
| send(options: MessageOptions): Promise<string>; | ||
| /** | ||
| * Sends a message to this session and waits until the session becomes idle. | ||
| * | ||
| * This is a convenience method that combines {@link send} with waiting for | ||
| * the `session.idle` event. Use this when you want to block until the | ||
| * assistant has finished processing the message. | ||
| * | ||
| * Events are still delivered to handlers registered via {@link on} while waiting. | ||
| * | ||
| * @param options - The message options including the prompt and optional attachments | ||
| * @param timeout - Timeout in milliseconds (default: 60000). Controls how long to wait; does not abort in-flight agent work. | ||
| * @returns A promise that resolves with the final assistant message when the session becomes idle, | ||
| * or undefined if no assistant message was received | ||
| * @throws Error if the timeout is reached before the session becomes idle | ||
| * @throws Error if the session has been disconnected or the connection fails | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * // Send and wait for completion with default 60s timeout | ||
| * const response = await session.sendAndWait({ prompt: "What is 2+2?" }); | ||
| * console.log(response?.data.content); // "4" | ||
| * ``` | ||
| */ | ||
| sendAndWait(prompt: string, timeout?: number): Promise<AssistantMessageEvent | undefined>; | ||
| sendAndWait(options: MessageOptions, timeout?: number): Promise<AssistantMessageEvent | undefined>; | ||
| /** | ||
| * Subscribes to events from this session. | ||
| * | ||
| * Events include assistant messages, tool executions, errors, and session state changes. | ||
| * Multiple handlers can be registered and will all receive events. | ||
| * | ||
| * @param eventType - The specific event type to listen for (e.g., "assistant.message", "session.idle") | ||
| * @param handler - A callback function that receives events of the specified type | ||
| * @returns A function that, when called, unsubscribes the handler | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * // Listen for a specific event type | ||
| * const unsubscribe = session.on("assistant.message", (event) => { | ||
| * console.log("Assistant:", event.data.content); | ||
| * }); | ||
| * | ||
| * // Later, to stop receiving events: | ||
| * unsubscribe(); | ||
| * ``` | ||
| */ | ||
| on<K extends SessionEventType>(eventType: K, handler: TypedSessionEventHandler<K>): () => void; | ||
| /** | ||
| * Subscribes to all events from this session. | ||
| * | ||
| * @param handler - A callback function that receives all session events | ||
| * @returns A function that, when called, unsubscribes the handler | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * const unsubscribe = session.on((event) => { | ||
| * switch (event.type) { | ||
| * case "assistant.message": | ||
| * console.log("Assistant:", event.data.content); | ||
| * break; | ||
| * case "session.error": | ||
| * console.error("Error:", event.data.message); | ||
| * break; | ||
| * } | ||
| * }); | ||
| * | ||
| * // Later, to stop receiving events: | ||
| * unsubscribe(); | ||
| * ``` | ||
| */ | ||
| on(handler: SessionEventHandler): () => void; | ||
| /** | ||
| * Snapshot of canvas instances that were already open when the session was | ||
| * resumed. Populated from the `session.resume` response; empty for freshly | ||
| * created sessions. Returns a defensive copy — mutating the returned array | ||
| * has no effect on the session. | ||
| */ | ||
| get openCanvases(): OpenCanvasInstance[]; | ||
| private assertElicitation; | ||
| private _elicitation; | ||
| private _confirm; | ||
| private _select; | ||
| private _input; | ||
| /** | ||
| * Retrieves all events and messages from this session's history. | ||
| * | ||
| * This returns the complete conversation history including user messages, | ||
| * assistant responses, tool executions, and other session events. | ||
| * | ||
| * @returns A promise that resolves with an array of all session events | ||
| * @throws Error if the session has been disconnected or the connection fails | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * const events = await session.getEvents(); | ||
| * for (const event of events) { | ||
| * if (event.type === "assistant.message") { | ||
| * console.log("Assistant:", event.data.content); | ||
| * } | ||
| * } | ||
| * ``` | ||
| */ | ||
| getEvents(): Promise<SessionEvent[]>; | ||
| /** | ||
| * Disconnects this session and releases all in-memory resources (event handlers, | ||
| * tool handlers, permission handlers). | ||
| * | ||
| * Session state on disk (conversation history, planning state, artifacts) is | ||
| * preserved, so the conversation can be resumed later by calling | ||
| * {@link CopilotClient.resumeSession} with the session ID. To permanently | ||
| * remove all session data including files on disk, use | ||
| * {@link CopilotClient.deleteSession} instead. | ||
| * | ||
| * After calling this method, the session object can no longer be used. | ||
| * | ||
| * @returns A promise that resolves when the session is disconnected | ||
| * @throws Error if the connection fails | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * // Clean up when done — session can still be resumed later | ||
| * await session.disconnect(); | ||
| * ``` | ||
| */ | ||
| disconnect(): Promise<void>; | ||
| /** Enables `await using session = ...` syntax for automatic cleanup. */ | ||
| [Symbol.asyncDispose](): Promise<void>; | ||
| /** | ||
| * Aborts the currently processing message in this session. | ||
| * | ||
| * Use this to cancel a long-running request. The session remains valid | ||
| * and can continue to be used for new messages. | ||
| * | ||
| * @returns A promise that resolves when the abort request is acknowledged | ||
| * @throws Error if the session has been disconnected or the connection fails | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * // Start a long-running request | ||
| * const messagePromise = session.send({ prompt: "Write a very long story..." }); | ||
| * | ||
| * // Abort after 5 seconds | ||
| * setTimeout(async () => { | ||
| * await session.abort(); | ||
| * }, 5000); | ||
| * ``` | ||
| */ | ||
| abort(): Promise<void>; | ||
| /** | ||
| * Change the model for this session. | ||
| * The new model takes effect for the next message. Conversation history is preserved. | ||
| * | ||
| * @param model - Model ID to switch to | ||
| * @param options - Optional settings for the new model | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * await session.setModel("gpt-4.1"); | ||
| * await session.setModel("claude-sonnet-4.6", { reasoningEffort: "high" }); | ||
| * ``` | ||
| */ | ||
| setModel(model: string, options?: { | ||
| reasoningEffort?: ReasoningEffort; | ||
| modelCapabilities?: ModelCapabilitiesOverride; | ||
| }): Promise<void>; | ||
| /** | ||
| * Log a message to the session timeline. | ||
| * The message appears in the session event stream and is visible to SDK consumers | ||
| * and (for non-ephemeral messages) persisted to the session event log on disk. | ||
| * | ||
| * @param message - Human-readable message text | ||
| * @param options - Optional log level and ephemeral flag | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * await session.log("Processing started"); | ||
| * await session.log("Disk usage high", { level: "warning" }); | ||
| * await session.log("Connection failed", { level: "error" }); | ||
| * await session.log("Debug info", { ephemeral: true }); | ||
| * ``` | ||
| */ | ||
| log(message: string, options?: { | ||
| level?: "info" | "warning" | "error"; | ||
| ephemeral?: boolean; | ||
| }): Promise<void>; | ||
| } |
| import type { SessionFsHandler, SessionFsStatResult, SessionFsReaddirWithTypesEntry, SessionFsSqliteQueryResult as GeneratedSqliteQueryResult, SessionFsSqliteQueryType } from "./generated/rpc.js"; | ||
| export type { SessionFsSqliteQueryType }; | ||
| /** | ||
| * File metadata returned by {@link SessionFsProvider.stat}. | ||
| * Same shape as the generated {@link SessionFsStatResult} but without the | ||
| * `error` field, since providers signal errors by throwing. | ||
| */ | ||
| export type SessionFsFileInfo = Omit<SessionFsStatResult, "error">; | ||
| /** | ||
| * Result of a SQLite query execution via {@link SessionFsSqliteProvider.query}. | ||
| * Same shape as the generated {@link GeneratedSqliteQueryResult} but without the | ||
| * `error` field, since providers signal errors by throwing. | ||
| */ | ||
| export type SessionFsSqliteQueryResult = Omit<GeneratedSqliteQueryResult, "error">; | ||
| /** | ||
| * SQLite operations for the per-session database. | ||
| * Implementers provide query execution and existence checking. | ||
| */ | ||
| export interface SessionFsSqliteProvider { | ||
| /** | ||
| * Execute a SQLite query against the per-session database. | ||
| * | ||
| * @param queryType - How to execute: `"exec"` for DDL/multi-statement, `"query"` for SELECT, `"run"` for INSERT/UPDATE/DELETE. | ||
| * @param query - SQL query to execute. | ||
| * @param params - Optional named bind parameters. | ||
| */ | ||
| query(queryType: SessionFsSqliteQueryType, query: string, params?: Record<string, string | number | null>): Promise<SessionFsSqliteQueryResult | undefined>; | ||
| /** | ||
| * Check whether the per-session database already exists, without creating it. | ||
| */ | ||
| exists(): Promise<boolean>; | ||
| } | ||
| /** | ||
| * Interface for session filesystem providers. Implementers use idiomatic | ||
| * TypeScript patterns: throw on error, return values directly. Use | ||
| * {@link createSessionFsAdapter} to convert a provider into the | ||
| * {@link SessionFsHandler} expected by the SDK. | ||
| * | ||
| * Errors with a `code` property of `"ENOENT"` are mapped to the ENOENT | ||
| * error code; all others map to UNKNOWN. | ||
| */ | ||
| export interface SessionFsProvider { | ||
| /** Reads the full content of a file. Throw if the file does not exist. */ | ||
| readFile(path: string): Promise<string>; | ||
| /** Writes content to a file, creating parent directories if needed. */ | ||
| writeFile(path: string, content: string, mode?: number): Promise<void>; | ||
| /** Appends content to a file, creating parent directories if needed. */ | ||
| appendFile(path: string, content: string, mode?: number): Promise<void>; | ||
| /** Checks whether a path exists. */ | ||
| exists(path: string): Promise<boolean>; | ||
| /** Gets metadata about a file or directory. Throw if it does not exist. */ | ||
| stat(path: string): Promise<SessionFsFileInfo>; | ||
| /** Creates a directory. If recursive is true, creates parents as needed. */ | ||
| mkdir(path: string, recursive: boolean, mode?: number): Promise<void>; | ||
| /** Lists entry names in a directory. Throw if it does not exist. */ | ||
| readdir(path: string): Promise<string[]>; | ||
| /** Lists entries with type info. Throw if the directory does not exist. */ | ||
| readdirWithTypes(path: string): Promise<SessionFsReaddirWithTypesEntry[]>; | ||
| /** Removes a file or directory. If force is true, do not throw on ENOENT. */ | ||
| rm(path: string, recursive: boolean, force: boolean): Promise<void>; | ||
| /** Renames/moves a file or directory. */ | ||
| rename(src: string, dest: string): Promise<void>; | ||
| /** Per-session SQLite database operations. Optional — omit if the provider does not support SQLite. */ | ||
| sqlite?: SessionFsSqliteProvider; | ||
| } | ||
| /** | ||
| * Wraps a {@link SessionFsProvider} into the {@link SessionFsHandler} | ||
| * interface expected by the SDK, converting thrown errors into | ||
| * {@link SessionFsError} results. | ||
| */ | ||
| export declare function createSessionFsAdapter(provider: SessionFsProvider): SessionFsHandler; |
| /** | ||
| * Trace-context helpers. | ||
| * | ||
| * The SDK does not depend on any OpenTelemetry packages. Instead, users | ||
| * provide an {@link TraceContextProvider} callback via client options. | ||
| * | ||
| * @module telemetry | ||
| */ | ||
| import type { TraceContext, TraceContextProvider } from "./types.js"; | ||
| /** | ||
| * Calls the user-provided {@link TraceContextProvider} to obtain the current | ||
| * W3C Trace Context. Returns `{}` when no provider is configured. | ||
| */ | ||
| export declare function getTraceContext(provider?: TraceContextProvider): Promise<TraceContext>; |
| /** | ||
| * Type definitions for the Copilot SDK | ||
| */ | ||
| import type { Canvas } from "./canvas.js"; | ||
| import type { SessionFsProvider } from "./sessionFsProvider.js"; | ||
| import type { SessionEvent as GeneratedSessionEvent } from "./generated/session-events.js"; | ||
| import type { CopilotSession } from "./session.js"; | ||
| import type { RemoteSessionMode } from "./generated/rpc.js"; | ||
| import type { OpenCanvasInstance } from "./generated/rpc.js"; | ||
| export type { RemoteSessionMode } from "./generated/rpc.js"; | ||
| export type SessionEvent = GeneratedSessionEvent; | ||
| export type { SessionFsProvider } from "./sessionFsProvider.js"; | ||
| export { createSessionFsAdapter } from "./sessionFsProvider.js"; | ||
| export type { SessionFsFileInfo } from "./sessionFsProvider.js"; | ||
| export type { SessionFsSqliteQueryResult } from "./sessionFsProvider.js"; | ||
| export type { SessionFsSqliteQueryType } from "./sessionFsProvider.js"; | ||
| export type { SessionFsSqliteProvider } from "./sessionFsProvider.js"; | ||
| /** | ||
| * Options for creating a CopilotClient | ||
| */ | ||
| /** | ||
| * W3C Trace Context headers used for distributed trace propagation. | ||
| */ | ||
| export interface TraceContext { | ||
| traceparent?: string; | ||
| tracestate?: string; | ||
| } | ||
| /** | ||
| * Callback that returns the current W3C Trace Context. | ||
| * Wire this up to your OpenTelemetry (or other tracing) SDK to enable | ||
| * distributed trace propagation between your app and the Copilot CLI. | ||
| */ | ||
| export type TraceContextProvider = () => TraceContext | Promise<TraceContext>; | ||
| /** | ||
| * Configuration for OpenTelemetry instrumentation. | ||
| * | ||
| * When provided via {@link CopilotClientOptions.telemetry}, the SDK sets | ||
| * the corresponding environment variables on the spawned CLI process so | ||
| * that the CLI's built-in OTel exporter is configured automatically. | ||
| */ | ||
| export interface TelemetryConfig { | ||
| /** OTLP HTTP endpoint URL for trace/metric export. Sets OTEL_EXPORTER_OTLP_ENDPOINT. */ | ||
| otlpEndpoint?: string; | ||
| /** File path for JSON-lines trace output. Sets COPILOT_OTEL_FILE_EXPORTER_PATH. */ | ||
| filePath?: string; | ||
| /** Exporter backend type: "otlp-http" or "file". Sets COPILOT_OTEL_EXPORTER_TYPE. */ | ||
| exporterType?: string; | ||
| /** Instrumentation scope name. Sets COPILOT_OTEL_SOURCE_NAME. */ | ||
| sourceName?: string; | ||
| /** Whether to capture message content (prompts, responses). Sets OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT. */ | ||
| captureContent?: boolean; | ||
| } | ||
| /** | ||
| * Configures how a {@link CopilotClient} connects to the Copilot runtime. | ||
| * Construct via the factory functions on {@link RuntimeConnection}. | ||
| */ | ||
| export type RuntimeConnection = StdioRuntimeConnection | TcpRuntimeConnection | UriRuntimeConnection; | ||
| /** | ||
| * Spawns a runtime child process and communicates over its stdin/stdout. | ||
| * This is the default if no {@link CopilotClientOptions.connection} is set. | ||
| */ | ||
| export interface StdioRuntimeConnection { | ||
| readonly kind: "stdio"; | ||
| /** Path to the runtime executable. When omitted, the bundled runtime is used. */ | ||
| readonly path?: string; | ||
| /** Extra command-line arguments to pass to the runtime process. */ | ||
| readonly args?: readonly string[]; | ||
| } | ||
| /** | ||
| * Spawns a runtime child process that listens on a TCP socket and connects to it. | ||
| */ | ||
| export interface TcpRuntimeConnection { | ||
| readonly kind: "tcp"; | ||
| /** | ||
| * TCP port to listen on. `0` (the default) auto-allocates a free port. | ||
| * If the chosen port is already in use, startup fails. | ||
| */ | ||
| readonly port?: number; | ||
| /** | ||
| * Optional shared secret the SDK sends to the spawned runtime to authenticate | ||
| * the TCP connection. When omitted, a UUID is generated automatically so the | ||
| * loopback listener is safe by default. | ||
| */ | ||
| readonly connectionToken?: string; | ||
| /** Path to the runtime executable. When omitted, the bundled runtime is used. */ | ||
| readonly path?: string; | ||
| /** Extra command-line arguments to pass to the runtime process. */ | ||
| readonly args?: readonly string[]; | ||
| } | ||
| /** | ||
| * Connects to an already-running runtime at the specified URL. The SDK does not | ||
| * spawn a process in this mode. | ||
| */ | ||
| export interface UriRuntimeConnection { | ||
| readonly kind: "uri"; | ||
| /** | ||
| * URL of the runtime to connect to. Accepts `"port"`, `"host:port"`, or a | ||
| * full URL (`"http://host:port"`). | ||
| */ | ||
| readonly url: string; | ||
| /** Optional shared secret to authenticate the connection. */ | ||
| readonly connectionToken?: string; | ||
| } | ||
| /** Factory functions for constructing {@link RuntimeConnection} instances. */ | ||
| export declare const RuntimeConnection: { | ||
| /** | ||
| * Spawn a runtime child process and communicate over its stdin/stdout. | ||
| * This is the default if no {@link CopilotClientOptions.connection} is set. | ||
| */ | ||
| readonly forStdio: (opts?: { | ||
| path?: string; | ||
| args?: readonly string[]; | ||
| }) => StdioRuntimeConnection; | ||
| /** | ||
| * Spawn a runtime child process that listens on a TCP socket and connect to it. | ||
| */ | ||
| readonly forTcp: (opts?: { | ||
| port?: number; | ||
| connectionToken?: string; | ||
| path?: string; | ||
| args?: readonly string[]; | ||
| }) => TcpRuntimeConnection; | ||
| /** | ||
| * Connect to an already-running runtime at the given URL. The SDK does not | ||
| * spawn a process in this mode. | ||
| */ | ||
| readonly forUri: (url: string, opts?: { | ||
| connectionToken?: string; | ||
| }) => UriRuntimeConnection; | ||
| }; | ||
| export interface CopilotClientOptions { | ||
| /** | ||
| * How to connect to the Copilot runtime. When omitted, defaults to | ||
| * {@link RuntimeConnection.forStdio} with the bundled runtime. | ||
| */ | ||
| connection?: RuntimeConnection; | ||
| /** | ||
| * Working directory for the runtime process. | ||
| * If not set, inherits the current process's working directory. | ||
| */ | ||
| workingDirectory?: string; | ||
| /** | ||
| * Base directory for Copilot data (session state, config, etc.). | ||
| * Sets the COPILOT_HOME environment variable on the spawned runtime. | ||
| * When not set, the runtime defaults to ~/.copilot. | ||
| * Ignored when connecting to an existing runtime via {@link RuntimeConnection.forUri}. | ||
| */ | ||
| baseDirectory?: string; | ||
| /** | ||
| * Log level for the Copilot runtime. When omitted, the runtime uses its | ||
| * own default (currently `"info"`). | ||
| */ | ||
| logLevel?: "none" | "error" | "warning" | "info" | "debug" | "all"; | ||
| /** | ||
| * Environment variables to pass to the runtime process. If not set, inherits process.env. | ||
| */ | ||
| env?: Record<string, string | undefined>; | ||
| /** | ||
| * GitHub token to use for authentication. | ||
| * When provided, the token is passed to the runtime via environment variable. | ||
| * This takes priority over other authentication methods. | ||
| */ | ||
| gitHubToken?: string; | ||
| /** | ||
| * Whether to use the logged-in user for authentication. | ||
| * When true, the runtime will attempt to use stored OAuth tokens or gh CLI auth. | ||
| * When false, only explicit tokens (gitHubToken or environment variables) are used. | ||
| * @default true (but defaults to false when gitHubToken is provided) | ||
| */ | ||
| useLoggedInUser?: boolean; | ||
| /** | ||
| * Custom handler for listing available models. | ||
| * When provided, client.listModels() calls this handler instead of | ||
| * querying the runtime. Useful in BYOK mode to return models | ||
| * available from your custom provider. | ||
| */ | ||
| onListModels?: () => Promise<ModelInfo[]> | ModelInfo[]; | ||
| /** | ||
| * OpenTelemetry configuration for the runtime process. | ||
| * When provided, the corresponding OTel environment variables are set | ||
| * on the spawned runtime. | ||
| */ | ||
| telemetry?: TelemetryConfig; | ||
| /** | ||
| * Advanced: callback that returns the current W3C Trace Context for distributed | ||
| * trace propagation. Most users do not need this — the {@link telemetry} config | ||
| * alone is sufficient to collect traces from the CLI. | ||
| * | ||
| * This callback is only useful when your application creates its own | ||
| * OpenTelemetry spans and you want them to appear in the **same** distributed | ||
| * trace as the CLI's spans. The SDK calls this before `session.create`, | ||
| * `session.resume`, and `session.send` RPCs to inject `traceparent`/`tracestate` | ||
| * into the request. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { propagation, context } from "@opentelemetry/api"; | ||
| * | ||
| * const client = new CopilotClient({ | ||
| * onGetTraceContext: () => { | ||
| * const carrier: Record<string, string> = {}; | ||
| * propagation.inject(context.active(), carrier); | ||
| * return carrier; | ||
| * }, | ||
| * }); | ||
| * ``` | ||
| */ | ||
| onGetTraceContext?: TraceContextProvider; | ||
| /** | ||
| * Custom session filesystem provider. | ||
| * When provided, the client registers as the session filesystem provider | ||
| * on connection, routing all session-scoped file I/O through these callbacks | ||
| * instead of the server's default local filesystem storage. | ||
| */ | ||
| sessionFs?: SessionFsConfig; | ||
| /** | ||
| * Server-wide idle timeout for sessions in seconds. | ||
| * Sessions without activity for this duration are automatically cleaned up. | ||
| * Set to 0 or omit to disable (sessions live indefinitely). | ||
| * Ignored when connecting to an existing runtime via {@link RuntimeConnection.forUri}. | ||
| * @default undefined (disabled) | ||
| */ | ||
| sessionIdleTimeoutSeconds?: number; | ||
| /** | ||
| * Enable remote session support (Mission Control integration). | ||
| * When true, sessions in a GitHub repository working directory are | ||
| * accessible from GitHub web and mobile. | ||
| * Ignored when connecting to an existing runtime via {@link RuntimeConnection.forUri}. | ||
| * @default false | ||
| */ | ||
| enableRemoteSessions?: boolean; | ||
| } | ||
| /** | ||
| * Configuration for creating a session | ||
| */ | ||
| export type ToolResultType = "success" | "failure" | "rejected" | "denied" | "timeout"; | ||
| export type ToolBinaryResult = { | ||
| data: string; | ||
| mimeType: string; | ||
| type: "image" | "resource"; | ||
| description?: string; | ||
| }; | ||
| export type ToolTelemetry = Record<string, Record<string, unknown> | undefined>; | ||
| export type ToolResultObject = { | ||
| textResultForLlm: string; | ||
| binaryResultsForLlm?: ToolBinaryResult[]; | ||
| resultType: ToolResultType; | ||
| error?: string; | ||
| sessionLog?: string; | ||
| toolTelemetry?: ToolTelemetry; | ||
| }; | ||
| export type ToolResult = string | ToolResultObject; | ||
| /** | ||
| * GitHub repository metadata to associate with a cloud session. | ||
| */ | ||
| export interface CloudSessionRepository { | ||
| owner: string; | ||
| name: string; | ||
| branch?: string; | ||
| } | ||
| /** | ||
| * Options for creating a remote session in the cloud. | ||
| */ | ||
| export interface CloudSessionOptions { | ||
| repository?: CloudSessionRepository; | ||
| } | ||
| /** | ||
| * Content block types within an MCP CallToolResult. | ||
| */ | ||
| type McpCallToolResultTextContent = { | ||
| type: "text"; | ||
| text: string; | ||
| }; | ||
| type McpCallToolResultImageContent = { | ||
| type: "image"; | ||
| data: string; | ||
| mimeType: string; | ||
| }; | ||
| type McpCallToolResultResourceContent = { | ||
| type: "resource"; | ||
| resource: { | ||
| uri: string; | ||
| mimeType?: string; | ||
| text?: string; | ||
| blob?: string; | ||
| }; | ||
| }; | ||
| type McpCallToolResultContent = McpCallToolResultTextContent | McpCallToolResultImageContent | McpCallToolResultResourceContent; | ||
| /** | ||
| * MCP-compatible CallToolResult type. Can be passed to | ||
| * {@link convertMcpCallToolResult} to produce a {@link ToolResultObject}. | ||
| */ | ||
| type McpCallToolResult = { | ||
| content: McpCallToolResultContent[]; | ||
| isError?: boolean; | ||
| }; | ||
| /** | ||
| * Converts an MCP CallToolResult into the SDK's ToolResultObject format. | ||
| */ | ||
| export declare function convertMcpCallToolResult(callResult: McpCallToolResult): ToolResultObject; | ||
| export interface ToolInvocation { | ||
| sessionId: string; | ||
| toolCallId: string; | ||
| toolName: string; | ||
| arguments: unknown; | ||
| /** W3C Trace Context traceparent from the CLI's execute_tool span. */ | ||
| traceparent?: string; | ||
| /** W3C Trace Context tracestate from the CLI's execute_tool span. */ | ||
| tracestate?: string; | ||
| } | ||
| export type ToolHandler<TArgs = unknown> = (args: TArgs, invocation: ToolInvocation) => Promise<unknown> | unknown; | ||
| /** | ||
| * Zod-like schema interface for type inference. | ||
| * Any object with `toJSONSchema()` method is treated as a Zod schema. | ||
| */ | ||
| export interface ZodSchema<T = unknown> { | ||
| _output: T; | ||
| toJSONSchema(): Record<string, unknown>; | ||
| } | ||
| /** | ||
| * Tool definition. Parameters can be either: | ||
| * - A Zod schema (provides type inference for handler) | ||
| * - A raw JSON schema object | ||
| * - Omitted (no parameters) | ||
| * | ||
| * If `handler` is omitted, the SDK exposes the declaration but does not | ||
| * automatically invoke the tool. Consumers can resolve tool calls by observing | ||
| * external tool request events and calling the pending-tool RPC. | ||
| */ | ||
| export interface Tool<TArgs = unknown> { | ||
| name: string; | ||
| description?: string; | ||
| parameters?: ZodSchema<TArgs> | Record<string, unknown>; | ||
| handler?: ToolHandler<TArgs>; | ||
| /** | ||
| * When true, explicitly indicates this tool is intended to override a built-in tool | ||
| * of the same name. If not set and the name clashes with a built-in tool, the runtime | ||
| * will return an error. | ||
| */ | ||
| overridesBuiltInTool?: boolean; | ||
| /** | ||
| * When true, the tool can execute without a permission prompt. | ||
| */ | ||
| skipPermission?: boolean; | ||
| } | ||
| /** | ||
| * Helper to define a tool with Zod schema and get type inference for the handler. | ||
| * Without this helper, TypeScript cannot infer handler argument types from Zod schemas. | ||
| */ | ||
| export declare function defineTool<T = unknown>(name: string, config: { | ||
| description?: string; | ||
| parameters?: ZodSchema<T> | Record<string, unknown>; | ||
| handler?: ToolHandler<T>; | ||
| overridesBuiltInTool?: boolean; | ||
| skipPermission?: boolean; | ||
| }): Tool<T>; | ||
| /** | ||
| * Context passed to a command handler when a command is executed. | ||
| */ | ||
| export interface CommandContext { | ||
| /** Session ID where the command was invoked */ | ||
| sessionId: string; | ||
| /** The full command text (e.g. "/deploy production") */ | ||
| command: string; | ||
| /** Command name without leading / */ | ||
| commandName: string; | ||
| /** Raw argument string after the command name */ | ||
| args: string; | ||
| } | ||
| /** | ||
| * Handler invoked when a registered command is executed by a user. | ||
| */ | ||
| export type CommandHandler = (context: CommandContext) => Promise<void> | void; | ||
| /** | ||
| * Definition of a slash command registered with the session. | ||
| * When the CLI is running with a TUI, registered commands appear as | ||
| * `/commandName` for the user to invoke. | ||
| */ | ||
| export interface CommandDefinition { | ||
| /** Command name (without leading /). */ | ||
| name: string; | ||
| /** Human-readable description shown in command completion UI. */ | ||
| description?: string; | ||
| /** Handler invoked when the command is executed. */ | ||
| handler: CommandHandler; | ||
| } | ||
| /** | ||
| * Capabilities reported by the CLI host for this session. | ||
| */ | ||
| export interface SessionCapabilities { | ||
| ui?: { | ||
| /** Whether the host supports interactive elicitation dialogs. */ | ||
| elicitation?: boolean; | ||
| /** Whether the host supports canvas rendering. */ | ||
| canvases?: boolean; | ||
| }; | ||
| } | ||
| /** | ||
| * A single field in an elicitation schema — matches the MCP SDK's | ||
| * `PrimitiveSchemaDefinition` union. | ||
| */ | ||
| export type ElicitationSchemaField = { | ||
| type: "string"; | ||
| title?: string; | ||
| description?: string; | ||
| enum: string[]; | ||
| enumNames?: string[]; | ||
| default?: string; | ||
| } | { | ||
| type: "string"; | ||
| title?: string; | ||
| description?: string; | ||
| oneOf: { | ||
| const: string; | ||
| title: string; | ||
| }[]; | ||
| default?: string; | ||
| } | { | ||
| type: "array"; | ||
| title?: string; | ||
| description?: string; | ||
| minItems?: number; | ||
| maxItems?: number; | ||
| items: { | ||
| type: "string"; | ||
| enum: string[]; | ||
| }; | ||
| default?: string[]; | ||
| } | { | ||
| type: "array"; | ||
| title?: string; | ||
| description?: string; | ||
| minItems?: number; | ||
| maxItems?: number; | ||
| items: { | ||
| anyOf: { | ||
| const: string; | ||
| title: string; | ||
| }[]; | ||
| }; | ||
| default?: string[]; | ||
| } | { | ||
| type: "boolean"; | ||
| title?: string; | ||
| description?: string; | ||
| default?: boolean; | ||
| } | { | ||
| type: "string"; | ||
| title?: string; | ||
| description?: string; | ||
| minLength?: number; | ||
| maxLength?: number; | ||
| format?: "email" | "uri" | "date" | "date-time"; | ||
| default?: string; | ||
| } | { | ||
| type: "number" | "integer"; | ||
| title?: string; | ||
| description?: string; | ||
| minimum?: number; | ||
| maximum?: number; | ||
| default?: number; | ||
| }; | ||
| /** | ||
| * Schema describing the form fields for an elicitation request. | ||
| */ | ||
| export interface ElicitationSchema { | ||
| type: "object"; | ||
| properties: Record<string, ElicitationSchemaField>; | ||
| required?: string[]; | ||
| } | ||
| /** | ||
| * Primitive field value in an elicitation result. | ||
| * Matches MCP SDK's `ElicitResult.content` value type. | ||
| */ | ||
| export type ElicitationFieldValue = string | number | boolean | string[]; | ||
| /** | ||
| * Result returned from an elicitation request. | ||
| */ | ||
| export interface ElicitationResult { | ||
| /** User action: "accept" (submitted), "decline" (rejected), or "cancel" (dismissed). */ | ||
| action: "accept" | "decline" | "cancel"; | ||
| /** Form values submitted by the user (present when action is "accept"). */ | ||
| content?: Record<string, ElicitationFieldValue>; | ||
| } | ||
| /** | ||
| * Parameters for a raw elicitation request. | ||
| */ | ||
| export interface ElicitationParams { | ||
| /** Message describing what information is needed from the user. */ | ||
| message: string; | ||
| /** JSON Schema describing the form fields to present. */ | ||
| requestedSchema: ElicitationSchema; | ||
| } | ||
| /** | ||
| * Context for an elicitation handler invocation, combining the request data | ||
| * with session context. Mirrors the single-argument pattern of {@link CommandContext}. | ||
| */ | ||
| export interface ElicitationContext { | ||
| /** Identifier of the session that triggered the elicitation request. */ | ||
| sessionId: string; | ||
| /** Message describing what information is needed from the user. */ | ||
| message: string; | ||
| /** JSON Schema describing the form fields to present. */ | ||
| requestedSchema?: ElicitationSchema; | ||
| /** Elicitation mode: "form" for structured input, "url" for browser redirect. */ | ||
| mode?: "form" | "url"; | ||
| /** The source that initiated the request (e.g. MCP server name). */ | ||
| elicitationSource?: string; | ||
| /** URL to open in the user's browser (url mode only). */ | ||
| url?: string; | ||
| } | ||
| /** | ||
| * Handler invoked when the server dispatches an elicitation request to this client. | ||
| * Return an {@link ElicitationResult} with the user's response. | ||
| */ | ||
| export type ElicitationHandler = (context: ElicitationContext) => Promise<ElicitationResult> | ElicitationResult; | ||
| /** | ||
| * Options for the `input()` convenience method. | ||
| */ | ||
| export interface UiInputOptions { | ||
| /** Title label for the input field. */ | ||
| title?: string; | ||
| /** Descriptive text shown below the field. */ | ||
| description?: string; | ||
| /** Minimum character length. */ | ||
| minLength?: number; | ||
| /** Maximum character length. */ | ||
| maxLength?: number; | ||
| /** Semantic format hint. */ | ||
| format?: "email" | "uri" | "date" | "date-time"; | ||
| /** Default value pre-populated in the field. */ | ||
| default?: string; | ||
| } | ||
| /** | ||
| * The `session.ui` API object providing interactive UI methods. | ||
| * Only usable when the CLI host supports elicitation. | ||
| */ | ||
| export interface SessionUiApi { | ||
| /** | ||
| * Shows a generic elicitation dialog with a custom schema. | ||
| * @throws Error if the host does not support elicitation. | ||
| */ | ||
| elicitation(params: ElicitationParams): Promise<ElicitationResult>; | ||
| /** | ||
| * Shows a confirmation dialog and returns the user's boolean answer. | ||
| * Returns `false` if the user declines or cancels. | ||
| * @throws Error if the host does not support elicitation. | ||
| */ | ||
| confirm(message: string): Promise<boolean>; | ||
| /** | ||
| * Shows a selection dialog with the given options. | ||
| * Returns the selected value, or `null` if the user declines/cancels. | ||
| * @throws Error if the host does not support elicitation. | ||
| */ | ||
| select(message: string, options: string[]): Promise<string | null>; | ||
| /** | ||
| * Shows a text input dialog. | ||
| * Returns the entered text, or `null` if the user declines/cancels. | ||
| * @throws Error if the host does not support elicitation. | ||
| */ | ||
| input(message: string, options?: UiInputOptions): Promise<string | null>; | ||
| } | ||
| export interface ToolCallRequestPayload { | ||
| sessionId: string; | ||
| toolCallId: string; | ||
| toolName: string; | ||
| arguments: unknown; | ||
| } | ||
| export interface ToolCallResponsePayload { | ||
| result: ToolResult; | ||
| } | ||
| /** | ||
| * Known system message section identifiers for the "customize" mode. | ||
| * Each section corresponds to a distinct part of the system prompt. | ||
| */ | ||
| export type SystemMessageSection = "identity" | "tone" | "tool_efficiency" | "environment_context" | "code_change_rules" | "guidelines" | "safety" | "tool_instructions" | "custom_instructions" | "runtime_instructions" | "last_instructions"; | ||
| /** Section metadata for documentation and tooling. */ | ||
| export declare const SYSTEM_MESSAGE_SECTIONS: Record<SystemMessageSection, { | ||
| description: string; | ||
| }>; | ||
| /** | ||
| * Transform callback for a single section: receives current content, returns new content. | ||
| */ | ||
| export type SectionTransformFn = (currentContent: string) => string | Promise<string>; | ||
| /** | ||
| * Override action: a string literal for static overrides, or a callback for transforms. | ||
| * | ||
| * - `"replace"`: Replace section content entirely | ||
| * - `"remove"`: Remove the section | ||
| * - `"append"`: Append to existing section content | ||
| * - `"prepend"`: Prepend to existing section content | ||
| * - `function`: Transform callback — receives current section content, returns new content | ||
| */ | ||
| export type SectionOverrideAction = "replace" | "remove" | "append" | "prepend" | SectionTransformFn; | ||
| /** | ||
| * Override operation for a single system message section. | ||
| */ | ||
| export interface SectionOverride { | ||
| /** | ||
| * The operation to perform on this section. | ||
| * Can be a string action or a transform callback function. | ||
| */ | ||
| action: SectionOverrideAction; | ||
| /** | ||
| * Content for the override. Optional for all actions. | ||
| * - For replace, omitting content replaces with an empty string. | ||
| * - For append/prepend, content is added before/after the existing section. | ||
| * - Ignored for the remove action. | ||
| */ | ||
| content?: string; | ||
| } | ||
| /** | ||
| * Append mode: Use CLI foundation with optional appended content (default). | ||
| */ | ||
| export interface SystemMessageAppendConfig { | ||
| mode?: "append"; | ||
| /** | ||
| * Additional instructions appended after SDK-managed sections. | ||
| */ | ||
| content?: string; | ||
| } | ||
| /** | ||
| * Replace mode: Use caller-provided system message entirely. | ||
| * Removes all SDK guardrails including security restrictions. | ||
| */ | ||
| export interface SystemMessageReplaceConfig { | ||
| mode: "replace"; | ||
| /** | ||
| * Complete system message content. | ||
| * Replaces the entire SDK-managed system message. | ||
| */ | ||
| content: string; | ||
| } | ||
| /** | ||
| * Customize mode: Override individual sections of the system prompt. | ||
| * Keeps the SDK-managed prompt structure while allowing targeted modifications. | ||
| */ | ||
| export interface SystemMessageCustomizeConfig { | ||
| mode: "customize"; | ||
| /** | ||
| * Override specific sections of the system prompt by section ID. | ||
| * Unknown section IDs gracefully fall back: content-bearing overrides are appended | ||
| * to additional instructions, and "remove" on unknown sections is a silent no-op. | ||
| */ | ||
| sections?: Partial<Record<SystemMessageSection, SectionOverride>>; | ||
| /** | ||
| * Additional content appended after all sections. | ||
| * Equivalent to append mode's content field — provided for convenience. | ||
| */ | ||
| content?: string; | ||
| } | ||
| /** | ||
| * System message configuration for session creation. | ||
| * - Append mode (default): SDK foundation + optional custom content | ||
| * - Replace mode: Full control, caller provides entire system message | ||
| * - Customize mode: Section-level overrides with graceful fallback | ||
| */ | ||
| export type SystemMessageConfig = SystemMessageAppendConfig | SystemMessageReplaceConfig | SystemMessageCustomizeConfig; | ||
| /** | ||
| * Permission request types from the server. This is the generated | ||
| * discriminated union from the runtime schema — switch on `kind` to | ||
| * access the variant-specific fields (e.g. shell `commands`, write | ||
| * `fileName`/`diff`, mcp `toolName`/`args`). | ||
| */ | ||
| export type { PermissionRequest } from "./generated/session-events.js"; | ||
| import type { PermissionRequest } from "./generated/session-events.js"; | ||
| import type { PermissionDecisionRequest } from "./generated/rpc.js"; | ||
| /** | ||
| * Permission decision result returned from a {@link PermissionHandler}. | ||
| * The discriminated `kind` field selects the decision. Variant-specific | ||
| * fields (e.g. `feedback` on `{ kind: "reject" }`) come from the generated | ||
| * `PermissionDecisionRequest["result"]` union. | ||
| */ | ||
| export type PermissionRequestResult = PermissionDecisionRequest["result"] | { | ||
| kind: "no-result"; | ||
| }; | ||
| export type PermissionHandler = (request: PermissionRequest, invocation: { | ||
| sessionId: string; | ||
| }) => Promise<PermissionRequestResult> | PermissionRequestResult; | ||
| export declare const approveAll: PermissionHandler; | ||
| export declare const defaultJoinSessionPermissionHandler: PermissionHandler; | ||
| /** | ||
| * Request for user input from the agent (enables ask_user tool) | ||
| */ | ||
| export interface UserInputRequest { | ||
| /** | ||
| * The question to ask the user | ||
| */ | ||
| question: string; | ||
| /** | ||
| * Optional choices for multiple choice questions | ||
| */ | ||
| choices?: string[]; | ||
| /** | ||
| * Whether to allow freeform text input in addition to choices | ||
| * @default true | ||
| */ | ||
| allowFreeform?: boolean; | ||
| } | ||
| /** | ||
| * Response to a user input request | ||
| */ | ||
| export interface UserInputResponse { | ||
| /** | ||
| * The user's answer | ||
| */ | ||
| answer: string; | ||
| /** | ||
| * Whether the answer was freeform (not from choices) | ||
| */ | ||
| wasFreeform: boolean; | ||
| } | ||
| /** | ||
| * Handler for user input requests from the agent | ||
| */ | ||
| export type UserInputHandler = (request: UserInputRequest, invocation: { | ||
| sessionId: string; | ||
| }) => Promise<UserInputResponse> | UserInputResponse; | ||
| /** | ||
| * Request to exit plan mode and continue with a selected action. | ||
| */ | ||
| export interface ExitPlanModeRequest { | ||
| /** Summary of the plan or proposed next step. */ | ||
| summary: string; | ||
| /** Full plan content, when available. */ | ||
| planContent?: string; | ||
| /** Available actions the user can select. */ | ||
| actions: string[]; | ||
| /** The action recommended by the runtime. */ | ||
| recommendedAction: string; | ||
| } | ||
| /** | ||
| * Response to an exit-plan-mode request. | ||
| */ | ||
| export interface ExitPlanModeResult { | ||
| /** Whether the user approved exiting plan mode. */ | ||
| approved: boolean; | ||
| /** Selected action, if the user chose one. */ | ||
| selectedAction?: string; | ||
| /** Optional feedback provided by the user. */ | ||
| feedback?: string; | ||
| } | ||
| /** | ||
| * Handler for exit-plan-mode requests from the agent. | ||
| */ | ||
| export type ExitPlanModeHandler = (request: ExitPlanModeRequest, invocation: { | ||
| sessionId: string; | ||
| }) => Promise<ExitPlanModeResult> | ExitPlanModeResult; | ||
| /** | ||
| * Request to switch to auto mode after an eligible rate limit. | ||
| */ | ||
| export interface AutoModeSwitchRequest { | ||
| /** The rate-limit error code that triggered the request. */ | ||
| errorCode?: string; | ||
| /** Seconds until the rate limit resets, when known. */ | ||
| retryAfterSeconds?: number; | ||
| } | ||
| /** | ||
| * Response to an auto-mode-switch request. | ||
| */ | ||
| export type AutoModeSwitchResponse = "yes" | "yes_always" | "no"; | ||
| /** | ||
| * Handler for auto-mode-switch requests from the agent. | ||
| */ | ||
| export type AutoModeSwitchHandler = (request: AutoModeSwitchRequest, invocation: { | ||
| sessionId: string; | ||
| }) => Promise<AutoModeSwitchResponse> | AutoModeSwitchResponse; | ||
| /** | ||
| * Base interface for all hook inputs | ||
| */ | ||
| export interface BaseHookInput { | ||
| /** The runtime session ID of the session that triggered the hook. | ||
| * For sub-agent hooks this differs from `invocation.sessionId`. */ | ||
| sessionId: string; | ||
| /** Time at which the hook event was emitted by the runtime. */ | ||
| timestamp: Date; | ||
| workingDirectory: string; | ||
| } | ||
| /** | ||
| * Input for pre-tool-use hook | ||
| */ | ||
| export interface PreToolUseHookInput extends BaseHookInput { | ||
| toolName: string; | ||
| toolArgs: unknown; | ||
| } | ||
| /** | ||
| * Output for pre-tool-use hook | ||
| */ | ||
| export interface PreToolUseHookOutput { | ||
| permissionDecision?: "allow" | "deny" | "ask"; | ||
| permissionDecisionReason?: string; | ||
| modifiedArgs?: unknown; | ||
| additionalContext?: string; | ||
| suppressOutput?: boolean; | ||
| } | ||
| /** | ||
| * Handler for pre-tool-use hook | ||
| */ | ||
| export type PreToolUseHandler = (input: PreToolUseHookInput, invocation: { | ||
| sessionId: string; | ||
| }) => Promise<PreToolUseHookOutput | void> | PreToolUseHookOutput | void; | ||
| /** | ||
| * Input for pre-MCP-tool-call hook | ||
| */ | ||
| export interface PreMcpToolCallHookInput extends BaseHookInput { | ||
| toolCallId?: string; | ||
| serverName: string; | ||
| toolName: string; | ||
| arguments: unknown; | ||
| _meta?: Record<string, unknown>; | ||
| } | ||
| /** | ||
| * Output for pre-MCP-tool-call hook | ||
| */ | ||
| export interface PreMcpToolCallHookOutput { | ||
| /** | ||
| * Hook-controlled metadata to use for the outgoing MCP request. | ||
| * - undefined/absent: preserve the current request `_meta` | ||
| * - object: use this object as request `_meta` | ||
| * - null: omit `_meta` | ||
| */ | ||
| metaToUse?: Record<string, unknown> | null; | ||
| } | ||
| /** | ||
| * Handler for pre-MCP-tool-call hook | ||
| */ | ||
| export type PreMcpToolCallHandler = (input: PreMcpToolCallHookInput, invocation: { | ||
| sessionId: string; | ||
| }) => Promise<PreMcpToolCallHookOutput | void> | PreMcpToolCallHookOutput | void; | ||
| /** | ||
| * Input for post-tool-use hook | ||
| */ | ||
| export interface PostToolUseHookInput extends BaseHookInput { | ||
| toolName: string; | ||
| toolArgs: unknown; | ||
| toolResult: ToolResultObject; | ||
| } | ||
| /** | ||
| * Output for post-tool-use hook | ||
| */ | ||
| export interface PostToolUseHookOutput { | ||
| modifiedResult?: ToolResultObject; | ||
| additionalContext?: string; | ||
| suppressOutput?: boolean; | ||
| } | ||
| /** | ||
| * Handler for post-tool-use hook | ||
| */ | ||
| export type PostToolUseHandler = (input: PostToolUseHookInput, invocation: { | ||
| sessionId: string; | ||
| }) => Promise<PostToolUseHookOutput | void> | PostToolUseHookOutput | void; | ||
| /** | ||
| * Input for user-prompt-submitted hook | ||
| */ | ||
| export interface UserPromptSubmittedHookInput extends BaseHookInput { | ||
| prompt: string; | ||
| } | ||
| /** | ||
| * Output for user-prompt-submitted hook | ||
| */ | ||
| export interface UserPromptSubmittedHookOutput { | ||
| modifiedPrompt?: string; | ||
| additionalContext?: string; | ||
| suppressOutput?: boolean; | ||
| } | ||
| /** | ||
| * Handler for user-prompt-submitted hook | ||
| */ | ||
| export type UserPromptSubmittedHandler = (input: UserPromptSubmittedHookInput, invocation: { | ||
| sessionId: string; | ||
| }) => Promise<UserPromptSubmittedHookOutput | void> | UserPromptSubmittedHookOutput | void; | ||
| /** | ||
| * Input for session-start hook | ||
| */ | ||
| export interface SessionStartHookInput extends BaseHookInput { | ||
| source: "startup" | "resume" | "new"; | ||
| initialPrompt?: string; | ||
| } | ||
| /** | ||
| * Output for session-start hook | ||
| */ | ||
| export interface SessionStartHookOutput { | ||
| additionalContext?: string; | ||
| modifiedConfig?: Record<string, unknown>; | ||
| } | ||
| /** | ||
| * Handler for session-start hook | ||
| */ | ||
| export type SessionStartHandler = (input: SessionStartHookInput, invocation: { | ||
| sessionId: string; | ||
| }) => Promise<SessionStartHookOutput | void> | SessionStartHookOutput | void; | ||
| /** | ||
| * Input for session-end hook | ||
| */ | ||
| export interface SessionEndHookInput extends BaseHookInput { | ||
| reason: "complete" | "error" | "abort" | "timeout" | "user_exit"; | ||
| finalMessage?: string; | ||
| error?: string; | ||
| } | ||
| /** | ||
| * Output for session-end hook | ||
| */ | ||
| export interface SessionEndHookOutput { | ||
| suppressOutput?: boolean; | ||
| cleanupActions?: string[]; | ||
| sessionSummary?: string; | ||
| } | ||
| /** | ||
| * Handler for session-end hook | ||
| */ | ||
| export type SessionEndHandler = (input: SessionEndHookInput, invocation: { | ||
| sessionId: string; | ||
| }) => Promise<SessionEndHookOutput | void> | SessionEndHookOutput | void; | ||
| /** | ||
| * Input for error-occurred hook | ||
| */ | ||
| export interface ErrorOccurredHookInput extends BaseHookInput { | ||
| error: string; | ||
| errorContext: "model_call" | "tool_execution" | "system" | "user_input"; | ||
| recoverable: boolean; | ||
| } | ||
| /** | ||
| * Output for error-occurred hook | ||
| */ | ||
| export interface ErrorOccurredHookOutput { | ||
| suppressOutput?: boolean; | ||
| errorHandling?: "retry" | "skip" | "abort"; | ||
| retryCount?: number; | ||
| userNotification?: string; | ||
| } | ||
| /** | ||
| * Handler for error-occurred hook | ||
| */ | ||
| export type ErrorOccurredHandler = (input: ErrorOccurredHookInput, invocation: { | ||
| sessionId: string; | ||
| }) => Promise<ErrorOccurredHookOutput | void> | ErrorOccurredHookOutput | void; | ||
| /** | ||
| * Configuration for session hooks | ||
| */ | ||
| export interface SessionHooks { | ||
| /** | ||
| * Called before a tool is executed | ||
| */ | ||
| onPreToolUse?: PreToolUseHandler; | ||
| /** | ||
| * Called before an MCP tool is called | ||
| */ | ||
| onPreMcpToolCall?: PreMcpToolCallHandler; | ||
| /** | ||
| * Called after a tool is executed | ||
| */ | ||
| onPostToolUse?: PostToolUseHandler; | ||
| /** | ||
| * Called when the user submits a prompt | ||
| */ | ||
| onUserPromptSubmitted?: UserPromptSubmittedHandler; | ||
| /** | ||
| * Called when a session starts | ||
| */ | ||
| onSessionStart?: SessionStartHandler; | ||
| /** | ||
| * Called when a session ends | ||
| */ | ||
| onSessionEnd?: SessionEndHandler; | ||
| /** | ||
| * Called when an error occurs | ||
| */ | ||
| onErrorOccurred?: ErrorOccurredHandler; | ||
| } | ||
| /** | ||
| * Base interface for MCP server configuration. | ||
| */ | ||
| interface MCPServerConfigBase { | ||
| /** | ||
| * List of tools to include from this server. | ||
| * `undefined` (the default) or `["*"]` means include all tools. | ||
| * `[]` means include none. | ||
| */ | ||
| tools?: string[]; | ||
| /** | ||
| * Indicates the server type: "stdio" for local/subprocess servers, "http"/"sse" for remote servers. | ||
| * If not specified, defaults to "stdio". | ||
| */ | ||
| type?: string; | ||
| /** | ||
| * Optional timeout in milliseconds for tool calls to this server. | ||
| */ | ||
| timeout?: number; | ||
| } | ||
| /** | ||
| * Configuration for a local/stdio MCP server. | ||
| */ | ||
| export interface MCPStdioServerConfig extends MCPServerConfigBase { | ||
| type?: "local" | "stdio"; | ||
| command: string; | ||
| args?: string[]; | ||
| /** | ||
| * Environment variables to pass to the server. | ||
| */ | ||
| env?: Record<string, string>; | ||
| /** | ||
| * Working directory for the server process. | ||
| */ | ||
| workingDirectory?: string; | ||
| } | ||
| /** | ||
| * Configuration for a remote MCP server (HTTP or SSE). | ||
| */ | ||
| export interface MCPHTTPServerConfig extends MCPServerConfigBase { | ||
| type: "http" | "sse"; | ||
| /** | ||
| * URL of the remote server. | ||
| */ | ||
| url: string; | ||
| /** | ||
| * Optional HTTP headers to include in requests. | ||
| */ | ||
| headers?: Record<string, string>; | ||
| } | ||
| /** | ||
| * Union type for MCP server configurations. | ||
| */ | ||
| export type MCPServerConfig = MCPStdioServerConfig | MCPHTTPServerConfig; | ||
| /** | ||
| * Configuration for a custom agent. | ||
| */ | ||
| export interface CustomAgentConfig { | ||
| /** | ||
| * Unique name of the custom agent. | ||
| */ | ||
| name: string; | ||
| /** | ||
| * Display name for UI purposes. | ||
| */ | ||
| displayName?: string; | ||
| /** | ||
| * Description of what the agent does. | ||
| */ | ||
| description?: string; | ||
| /** | ||
| * List of tool names the agent can use. | ||
| * Use null or undefined for all tools. | ||
| */ | ||
| tools?: string[] | null; | ||
| /** | ||
| * The prompt content for the agent. | ||
| */ | ||
| prompt: string; | ||
| /** | ||
| * MCP servers specific to this agent. | ||
| */ | ||
| mcpServers?: Record<string, MCPServerConfig>; | ||
| /** | ||
| * Whether the agent should be available for model inference. | ||
| * @default true | ||
| */ | ||
| infer?: boolean; | ||
| /** | ||
| * List of skill names to preload into this agent's context. | ||
| * When set, the full content of each listed skill is eagerly injected into | ||
| * the agent's context at startup. Skills are resolved by name from the | ||
| * session's configured skill directories (`skillDirectories`). | ||
| * When omitted, no skills are injected (opt-in model). | ||
| */ | ||
| skills?: string[]; | ||
| /** | ||
| * Model identifier for this agent (e.g. "claude-haiku-4.5"). | ||
| * When set, the runtime will attempt to use this model for the agent, | ||
| * falling back to the parent session model if unavailable. | ||
| */ | ||
| model?: string; | ||
| } | ||
| /** | ||
| * Configuration for the default agent (the built-in agent that handles | ||
| * turns when no custom agent is selected). | ||
| * Use this to control tool visibility for the default agent independently of custom sub-agents. | ||
| */ | ||
| export interface DefaultAgentConfig { | ||
| /** | ||
| * List of tool names to exclude from the default agent. | ||
| * These tools remain available to custom sub-agents that reference them in their `tools` array. | ||
| * Use this to register tools that should only be accessed via delegation to sub-agents, | ||
| * keeping the default agent's context clean. | ||
| */ | ||
| excludedTools?: string[]; | ||
| } | ||
| /** | ||
| * Configuration for infinite sessions with automatic context compaction and workspace persistence. | ||
| * When enabled, sessions automatically manage context window limits through background compaction | ||
| * and persist state to a workspace directory. | ||
| */ | ||
| export interface InfiniteSessionConfig { | ||
| /** | ||
| * Whether infinite sessions are enabled. | ||
| * @default true | ||
| */ | ||
| enabled?: boolean; | ||
| /** | ||
| * Context utilization threshold (0.0-1.0) at which background compaction starts. | ||
| * Compaction runs asynchronously, allowing the session to continue processing. | ||
| * @default 0.80 | ||
| */ | ||
| backgroundCompactionThreshold?: number; | ||
| /** | ||
| * Context utilization threshold (0.0-1.0) at which the session blocks until compaction completes. | ||
| * This prevents context overflow when compaction hasn't finished in time. | ||
| * @default 0.95 | ||
| */ | ||
| bufferExhaustionThreshold?: number; | ||
| } | ||
| /** | ||
| * Valid reasoning effort levels for models that support it. | ||
| */ | ||
| export type ReasoningEffort = "low" | "medium" | "high" | "xhigh"; | ||
| /** | ||
| * Stable extension identity for session participants that provide canvases. | ||
| */ | ||
| export interface ExtensionInfo { | ||
| /** Extension namespace/source, e.g. "github-app". */ | ||
| source: string; | ||
| /** Stable provider name within the source namespace. */ | ||
| name: string; | ||
| } | ||
| /** | ||
| * Shared configuration fields used by both {@link SessionConfig} (for | ||
| * creating a new session) and {@link ResumeSessionConfig} (for resuming | ||
| * an existing one). | ||
| */ | ||
| export interface SessionConfigBase { | ||
| /** | ||
| * Client name to identify the application using the SDK. | ||
| * Included in the User-Agent header for API requests. | ||
| */ | ||
| clientName?: string; | ||
| /** | ||
| * Model to use for this session | ||
| */ | ||
| model?: string; | ||
| /** | ||
| * Reasoning effort level for models that support it. | ||
| * Only valid for models where capabilities.supports.reasoningEffort is true. | ||
| * Use client.listModels() to check supported values for each model. | ||
| */ | ||
| reasoningEffort?: ReasoningEffort; | ||
| /** Per-property overrides for model capabilities, deep-merged over runtime defaults. */ | ||
| modelCapabilities?: ModelCapabilitiesOverride; | ||
| /** | ||
| * Override the default configuration directory location. | ||
| * When specified, the session will use this directory for storing config and state. | ||
| */ | ||
| configDir?: string; | ||
| /** | ||
| * When true, automatically discovers MCP server configurations (e.g. `.mcp.json`, | ||
| * `.vscode/mcp.json`) and skill directories from the working directory and merges | ||
| * them with any explicitly provided `mcpServers` and `skillDirectories`, with | ||
| * explicit values taking precedence on name collision. | ||
| * | ||
| * Note: custom instruction files (`.github/copilot-instructions.md`, `AGENTS.md`, etc.) | ||
| * are always loaded from the working directory regardless of this setting. | ||
| * | ||
| * @default false | ||
| */ | ||
| enableConfigDiscovery?: boolean; | ||
| /** | ||
| * Tools exposed to the CLI server. Tools without a handler are declaration-only | ||
| * and must be resolved by the consumer via pending external tool request RPCs. | ||
| */ | ||
| tools?: Tool<any>[]; | ||
| /** | ||
| * Canvases contributed by this session participant. The declaring | ||
| * connection becomes the live provider for `canvas.open|focus|close|reload` | ||
| * and `canvas.action.invoke` dispatches targeting each canvas's `id` for | ||
| * the lifetime of the connection. Re-declaring the same id on resume | ||
| * replaces the prior declaration. | ||
| */ | ||
| canvases?: Canvas[]; | ||
| /** | ||
| * Renderer-side opt-in: when true, the runtime surfaces canvas agent tools | ||
| * (`list_canvas_capabilities`, `open_canvas`, `invoke_canvas_action`) to | ||
| * the model for this connection. Default off so SDK callers that cannot | ||
| * display canvases stay clean. | ||
| */ | ||
| requestCanvasRenderer?: boolean; | ||
| /** | ||
| * Extension surface opt-in: when true, the runtime wires extension | ||
| * management tools and per-extension tool dispatch onto the session for | ||
| * this connection. Default off so callers that do not expose extensions | ||
| * stay clean. | ||
| */ | ||
| requestExtensions?: boolean; | ||
| /** | ||
| * Stable extension identity for canvas providers on this connection. When | ||
| * set, the runtime uses `${source}:${name}` as the agent-facing extension | ||
| * id instead of a reconnect-specific connection id. | ||
| */ | ||
| extensionInfo?: ExtensionInfo; | ||
| /** | ||
| * Slash commands registered for this session. | ||
| * When the CLI has a TUI, each command appears as `/name` for the user to invoke. | ||
| * The handler is called when the user executes the command. | ||
| */ | ||
| commands?: CommandDefinition[]; | ||
| /** | ||
| * System message configuration | ||
| * Controls how the system prompt is constructed | ||
| */ | ||
| systemMessage?: SystemMessageConfig; | ||
| /** | ||
| * List of tool names to allow. When specified, only these tools will be available. | ||
| * Takes precedence over excludedTools. | ||
| */ | ||
| availableTools?: string[]; | ||
| /** | ||
| * List of tool names to disable. All other tools remain available. | ||
| * Ignored if availableTools is specified. | ||
| */ | ||
| excludedTools?: string[]; | ||
| /** | ||
| * Custom provider configuration (BYOK - Bring Your Own Key). | ||
| * When specified, uses the provided API endpoint instead of the Copilot API. | ||
| */ | ||
| provider?: ProviderConfig; | ||
| /** | ||
| * Enables or disables internal session telemetry for this session. | ||
| * When `false`, disables session telemetry. When omitted (the default) or `true`, | ||
| * telemetry is enabled for GitHub-authenticated sessions. | ||
| * When a custom {@link provider} (BYOK) is configured, session telemetry is always | ||
| * disabled regardless of this setting. | ||
| * This is independent of the OpenTelemetry configuration in {@link CopilotClientOptions.telemetry}. | ||
| */ | ||
| enableSessionTelemetry?: boolean; | ||
| /** | ||
| * Optional handler for permission requests from the server. | ||
| * When omitted, permission requests are surfaced as events and left pending for | ||
| * the consumer to resolve via the pending permission RPC. | ||
| */ | ||
| onPermissionRequest?: PermissionHandler; | ||
| /** | ||
| * Handler for user input requests from the agent. | ||
| * When provided, enables the ask_user tool allowing the agent to ask questions. | ||
| */ | ||
| onUserInputRequest?: UserInputHandler; | ||
| /** | ||
| * Handler for elicitation requests from the agent. | ||
| * When provided, the server calls back to this client for form-based UI dialogs. | ||
| * Also enables the `elicitation` capability on the session. | ||
| */ | ||
| onElicitationRequest?: ElicitationHandler; | ||
| /** | ||
| * Handler for exit-plan-mode requests from the agent. | ||
| * When provided, enables `exitPlanMode.request` callbacks. | ||
| */ | ||
| onExitPlanModeRequest?: ExitPlanModeHandler; | ||
| /** | ||
| * Handler for auto-mode-switch requests from the agent. | ||
| * When provided, enables `autoModeSwitch.request` callbacks. | ||
| */ | ||
| onAutoModeSwitchRequest?: AutoModeSwitchHandler; | ||
| /** | ||
| * Hook handlers for intercepting session lifecycle events. | ||
| * When provided, enables hooks callback allowing custom logic at various points. | ||
| */ | ||
| hooks?: SessionHooks; | ||
| /** | ||
| * Working directory for the session. | ||
| * Tool operations will be relative to this directory. | ||
| */ | ||
| workingDirectory?: string; | ||
| /** | ||
| * Enable streaming of assistant message and reasoning chunks. | ||
| * When true, ephemeral assistant.message_delta and assistant.reasoning_delta | ||
| * events are sent as the response is generated. Clients should accumulate | ||
| * deltaContent values to build the full response. | ||
| * @default false | ||
| */ | ||
| streaming?: boolean; | ||
| /** | ||
| * Include sub-agent streaming events in the event stream. When true, streaming | ||
| * delta events from sub-agents (e.g., `assistant.message_delta`, | ||
| * `assistant.reasoning_delta`, `assistant.streaming_delta` with `agentId` set) | ||
| * are forwarded to this connection. When false, only non-streaming sub-agent | ||
| * events and `subagent.*` lifecycle events are forwarded; streaming deltas from | ||
| * sub-agents are suppressed. | ||
| * @default true | ||
| */ | ||
| includeSubAgentStreamingEvents?: boolean; | ||
| /** | ||
| * MCP server configurations for the session. | ||
| * Keys are server names, values are server configurations. | ||
| */ | ||
| mcpServers?: Record<string, MCPServerConfig>; | ||
| /** | ||
| * Custom agent configurations for the session. | ||
| */ | ||
| customAgents?: CustomAgentConfig[]; | ||
| /** | ||
| * Configuration for the default agent (the built-in agent that handles | ||
| * turns when no custom agent is selected). | ||
| * Use `excludedTools` to hide specific tools from the default agent while keeping | ||
| * them available to custom sub-agents. | ||
| */ | ||
| defaultAgent?: DefaultAgentConfig; | ||
| /** | ||
| * Name of the custom agent to activate when the session starts. | ||
| * Must match the `name` of one of the agents in `customAgents`. | ||
| * Equivalent to calling `session.rpc.agent.select({ name })` after creation. | ||
| */ | ||
| agent?: string; | ||
| /** | ||
| * Directories to load skills from. | ||
| */ | ||
| skillDirectories?: string[]; | ||
| /** | ||
| * Additional directories to search for custom instruction files. | ||
| */ | ||
| instructionDirectories?: string[]; | ||
| /** | ||
| * List of skill names to disable. | ||
| */ | ||
| disabledSkills?: string[]; | ||
| /** | ||
| * Infinite session configuration for persistent workspaces and automatic compaction. | ||
| * When enabled (default), sessions automatically manage context limits and persist state. | ||
| * Set to `{ enabled: false }` to disable. | ||
| */ | ||
| infiniteSessions?: InfiniteSessionConfig; | ||
| /** | ||
| * GitHub token for per-session authentication. | ||
| * When provided, the runtime resolves this token into a full GitHub identity | ||
| * (login, Copilot plan, endpoints) and stores it on the session. This enables | ||
| * multitenancy — different sessions can have different GitHub identities. | ||
| * | ||
| * This is independent of the client-level `gitHubToken` in {@link CopilotClientOptions}, | ||
| * which authenticates the CLI process itself. The session-level token determines | ||
| * the identity used for content exclusion, model routing, and quota checks. | ||
| */ | ||
| gitHubToken?: string; | ||
| /** | ||
| * Per-session remote behavior control: | ||
| * - `"off"` — local only, no remote export (default) | ||
| * - `"export"` — export session events to GitHub without enabling remote steering | ||
| * - `"on"` — export to GitHub AND enable remote steering | ||
| */ | ||
| remoteSession?: RemoteSessionMode; | ||
| /** | ||
| * Optional event handler that is registered on the session before the | ||
| * session.create RPC is issued. This guarantees that early events emitted | ||
| * by the CLI during session creation (e.g. session.start) are delivered to | ||
| * the handler. | ||
| * | ||
| * Equivalent to calling `session.on(handler)` immediately after creation, | ||
| * but executes earlier in the lifecycle so no events are missed. | ||
| */ | ||
| onEvent?: SessionEventHandler; | ||
| /** | ||
| * Supplies a handler for session filesystem operations. This takes effect | ||
| * only if {@link CopilotClientOptions.sessionFs} is configured. | ||
| */ | ||
| createSessionFsProvider?: (session: CopilotSession) => SessionFsProvider; | ||
| } | ||
| /** | ||
| * Configuration for creating a new session via {@link CopilotClient.createSession}. | ||
| */ | ||
| export interface SessionConfig extends SessionConfigBase { | ||
| /** | ||
| * Optional custom session ID. If not provided, the server generates one. | ||
| */ | ||
| sessionId?: string; | ||
| /** | ||
| * Creates a remote session in the cloud instead of a local session. | ||
| * The optional repository is associated with the cloud session. | ||
| */ | ||
| cloud?: CloudSessionOptions; | ||
| } | ||
| /** | ||
| * Configuration for resuming an existing session via | ||
| * {@link CopilotClient.resumeSession}. | ||
| */ | ||
| export interface ResumeSessionConfig extends SessionConfigBase { | ||
| /** | ||
| * When true, skips emitting the session.resume event. | ||
| * Useful for reconnecting to a session without triggering resume-related side effects. | ||
| * @default false | ||
| */ | ||
| suppressResumeEvent?: boolean; | ||
| /** | ||
| * When true, the runtime continues any tool calls or permission prompts that were | ||
| * still pending when the session was last suspended. When false (the default), the | ||
| * runtime treats pending work as interrupted on resume. | ||
| * | ||
| * For permission requests, the runtime re-emits `permission.requested` so the | ||
| * registered `onPermissionRequest` handler can re-prompt; for external tool calls, | ||
| * the consumer is expected to supply the result via the corresponding low-level | ||
| * RPC method. | ||
| * @default false | ||
| */ | ||
| continuePendingWork?: boolean; | ||
| /** | ||
| * Snapshot of canvases that were already open when the session was suspended. | ||
| * When provided on resume, the runtime can rehydrate canvas state so consumers | ||
| * do not need to re-open canvases that were active before the previous shutdown. | ||
| */ | ||
| openCanvases?: OpenCanvasInstance[]; | ||
| } | ||
| /** | ||
| * Configuration for a custom API provider. | ||
| */ | ||
| export interface ProviderConfig { | ||
| /** | ||
| * Provider type. Defaults to "openai" for generic OpenAI-compatible APIs. | ||
| */ | ||
| type?: "openai" | "azure" | "anthropic"; | ||
| /** | ||
| * API format (openai/azure only). Defaults to "completions". | ||
| */ | ||
| wireApi?: "completions" | "responses"; | ||
| /** | ||
| * API endpoint URL | ||
| */ | ||
| baseUrl: string; | ||
| /** | ||
| * API key. Optional for local providers like Ollama. | ||
| */ | ||
| apiKey?: string; | ||
| /** | ||
| * Bearer token for authentication. Sets the Authorization header directly. | ||
| * Use this for services requiring bearer token auth instead of API key. | ||
| * Takes precedence over apiKey when both are set. | ||
| */ | ||
| bearerToken?: string; | ||
| /** | ||
| * Azure-specific options | ||
| */ | ||
| azure?: { | ||
| /** | ||
| * API version. Defaults to "2024-10-21". | ||
| */ | ||
| apiVersion?: string; | ||
| }; | ||
| /** | ||
| * Custom HTTP headers to include in outbound provider requests. | ||
| */ | ||
| headers?: Record<string, string>; | ||
| /** | ||
| * Well-known model name used by the runtime to look up agent configuration | ||
| * (tools, prompts, reasoning behavior) and default token limits. Also used | ||
| * as the wire model when {@link wireModel} is not set. | ||
| * Falls back to {@link SessionConfig.model}. | ||
| */ | ||
| modelId?: string; | ||
| /** | ||
| * Model name sent to the provider API for inference. Use this when the | ||
| * provider's model name (e.g. an Azure deployment name or a custom | ||
| * fine-tune name) differs from {@link modelId}. | ||
| * Falls back to {@link modelId}, then {@link SessionConfig.model}. | ||
| */ | ||
| wireModel?: string; | ||
| /** | ||
| * Overrides the resolved model's default max prompt tokens. The runtime | ||
| * triggers conversation compaction before sending a request when the | ||
| * prompt (system message, history, tool definitions, user message) would | ||
| * exceed this limit. | ||
| */ | ||
| maxPromptTokens?: number; | ||
| /** | ||
| * Overrides the resolved model's default max output tokens. When hit, the | ||
| * model stops generating and returns a truncated response. | ||
| */ | ||
| maxOutputTokens?: number; | ||
| } | ||
| /** | ||
| * Options for sending a message to a session | ||
| */ | ||
| export interface MessageOptions { | ||
| /** | ||
| * The prompt/message to send | ||
| */ | ||
| prompt: string; | ||
| /** | ||
| * File, directory, selection, or blob attachments | ||
| */ | ||
| attachments?: Array<{ | ||
| type: "file"; | ||
| path: string; | ||
| displayName?: string; | ||
| } | { | ||
| type: "directory"; | ||
| path: string; | ||
| displayName?: string; | ||
| } | { | ||
| type: "selection"; | ||
| filePath: string; | ||
| displayName: string; | ||
| selection?: { | ||
| start: { | ||
| line: number; | ||
| character: number; | ||
| }; | ||
| end: { | ||
| line: number; | ||
| character: number; | ||
| }; | ||
| }; | ||
| text?: string; | ||
| } | { | ||
| type: "blob"; | ||
| data: string; | ||
| mimeType: string; | ||
| displayName?: string; | ||
| }>; | ||
| /** | ||
| * Message delivery mode | ||
| * - "enqueue": Add to queue (default) | ||
| * - "immediate": Send immediately | ||
| */ | ||
| mode?: "enqueue" | "immediate"; | ||
| /** | ||
| * Custom HTTP headers to include in outbound model requests for this turn. | ||
| */ | ||
| requestHeaders?: Record<string, string>; | ||
| } | ||
| /** | ||
| * All possible event type strings from SessionEvent | ||
| */ | ||
| export type SessionEventType = SessionEvent["type"]; | ||
| /** | ||
| * Extract the specific event payload for a given event type | ||
| */ | ||
| export type SessionEventPayload<T extends SessionEventType> = Extract<SessionEvent, { | ||
| type: T; | ||
| }>; | ||
| /** | ||
| * Event handler for a specific event type | ||
| */ | ||
| export type TypedSessionEventHandler<T extends SessionEventType> = (event: SessionEventPayload<T>) => void; | ||
| /** | ||
| * Event handler callback type (for all events) | ||
| */ | ||
| export type SessionEventHandler = (event: SessionEvent) => void; | ||
| /** | ||
| * Working directory context for a session | ||
| */ | ||
| export interface SessionContext { | ||
| /** Working directory where the session was created */ | ||
| workingDirectory: string; | ||
| /** Git repository root (if in a git repo) */ | ||
| gitRoot?: string; | ||
| /** GitHub repository in "owner/repo" format */ | ||
| repository?: string; | ||
| /** Current git branch */ | ||
| branch?: string; | ||
| } | ||
| /** | ||
| * Configuration for a custom session filesystem provider. | ||
| */ | ||
| export interface SessionFsConfig { | ||
| /** | ||
| * Initial working directory for sessions (user's project directory). | ||
| */ | ||
| initialCwd: string; | ||
| /** | ||
| * Path within each session's SessionFs where the runtime stores | ||
| * session-scoped files (events, workspace, checkpoints, etc.). | ||
| */ | ||
| sessionStatePath: string; | ||
| /** | ||
| * Path conventions used by this filesystem provider. | ||
| */ | ||
| conventions: "windows" | "posix"; | ||
| /** | ||
| * Optional capabilities declared by this provider. | ||
| * The runtime uses these to determine which features are available. | ||
| */ | ||
| capabilities?: { | ||
| /** | ||
| * Whether this provider supports SQLite query/exists operations. | ||
| * When false or omitted, the runtime will not offer SQL tools or | ||
| * todo tracking for sessions using this provider. | ||
| * @default false | ||
| */ | ||
| sqlite?: boolean; | ||
| }; | ||
| } | ||
| /** | ||
| * Filter options for listing sessions | ||
| */ | ||
| export interface SessionListFilter { | ||
| /** Filter by exact working directory match */ | ||
| workingDirectory?: string; | ||
| /** Filter by git root */ | ||
| gitRoot?: string; | ||
| /** Filter by repository (owner/repo format) */ | ||
| repository?: string; | ||
| /** Filter by branch */ | ||
| branch?: string; | ||
| } | ||
| /** | ||
| * Metadata about a session | ||
| */ | ||
| export interface SessionMetadata { | ||
| sessionId: string; | ||
| startTime: Date; | ||
| modifiedTime: Date; | ||
| summary?: string; | ||
| isRemote: boolean; | ||
| /** Working directory context (working directory, git info) from session creation */ | ||
| context?: SessionContext; | ||
| } | ||
| /** | ||
| * Response from status.get | ||
| */ | ||
| export interface GetStatusResponse { | ||
| /** Package version (e.g., "1.0.0") */ | ||
| version: string; | ||
| /** Protocol version for SDK compatibility */ | ||
| protocolVersion: number; | ||
| } | ||
| /** | ||
| * Response from auth.getStatus | ||
| */ | ||
| export interface GetAuthStatusResponse { | ||
| /** Whether the user is authenticated */ | ||
| isAuthenticated: boolean; | ||
| /** Authentication type */ | ||
| authType?: "user" | "env" | "gh-cli" | "hmac" | "api-key" | "token"; | ||
| /** GitHub host URL */ | ||
| host?: string; | ||
| /** User login name */ | ||
| login?: string; | ||
| /** Human-readable status message */ | ||
| statusMessage?: string; | ||
| } | ||
| /** | ||
| * Model capabilities and limits | ||
| */ | ||
| export interface ModelCapabilities { | ||
| supports: { | ||
| vision: boolean; | ||
| /** Whether this model supports reasoning effort configuration */ | ||
| reasoningEffort: boolean; | ||
| }; | ||
| limits: { | ||
| max_prompt_tokens?: number; | ||
| max_context_window_tokens: number; | ||
| vision?: { | ||
| supported_media_types: string[]; | ||
| max_prompt_images: number; | ||
| max_prompt_image_size: number; | ||
| }; | ||
| }; | ||
| } | ||
| /** Recursively makes all properties optional, preserving arrays as-is. */ | ||
| type DeepPartial<T> = T extends readonly (infer U)[] ? DeepPartial<U>[] : T extends object ? { | ||
| [K in keyof T]?: DeepPartial<T[K]>; | ||
| } : T; | ||
| /** Deep-partial override for model capabilities — every property at any depth is optional. */ | ||
| export type ModelCapabilitiesOverride = DeepPartial<ModelCapabilities>; | ||
| /** | ||
| * Model policy state | ||
| */ | ||
| export interface ModelPolicy { | ||
| state: "enabled" | "disabled" | "unconfigured"; | ||
| terms: string; | ||
| } | ||
| /** | ||
| * Model billing information | ||
| */ | ||
| export interface ModelBilling { | ||
| multiplier?: number; | ||
| } | ||
| /** | ||
| * Information about an available model | ||
| */ | ||
| export interface ModelInfo { | ||
| /** Model identifier (e.g., "claude-sonnet-4.5") */ | ||
| id: string; | ||
| /** Display name */ | ||
| name: string; | ||
| /** Model capabilities and limits */ | ||
| capabilities: ModelCapabilities; | ||
| /** Policy state */ | ||
| policy?: ModelPolicy; | ||
| /** Billing information */ | ||
| billing?: ModelBilling; | ||
| /** Supported reasoning effort levels (only present if model supports reasoning effort) */ | ||
| supportedReasoningEfforts?: ReasoningEffort[]; | ||
| /** Default reasoning effort level (only present if model supports reasoning effort) */ | ||
| defaultReasoningEffort?: ReasoningEffort; | ||
| } | ||
| /** | ||
| * Types of session lifecycle events. | ||
| */ | ||
| export type SessionLifecycleEventType = "session.created" | "session.deleted" | "session.updated" | "session.foreground" | "session.background"; | ||
| /** | ||
| * Metadata payload for session lifecycle events. Not present on | ||
| * `session.deleted` events. | ||
| */ | ||
| export interface SessionLifecycleEventMetadata { | ||
| /** Time the session was created. */ | ||
| startTime: Date; | ||
| /** Time the session was last modified. */ | ||
| modifiedTime: Date; | ||
| /** Human-readable summary of the session, if available. */ | ||
| summary?: string; | ||
| } | ||
| /** Base shape shared by every lifecycle event variant. */ | ||
| interface SessionLifecycleEventBase { | ||
| /** ID of the session this event relates to. */ | ||
| sessionId: string; | ||
| /** Session metadata (not included for `session.deleted`). */ | ||
| metadata?: SessionLifecycleEventMetadata; | ||
| } | ||
| /** Emitted when a new session is created. */ | ||
| export interface SessionCreatedEvent extends SessionLifecycleEventBase { | ||
| type: "session.created"; | ||
| metadata: SessionLifecycleEventMetadata; | ||
| } | ||
| /** Emitted when a session is deleted. The metadata field is omitted. */ | ||
| export interface SessionDeletedEvent extends SessionLifecycleEventBase { | ||
| type: "session.deleted"; | ||
| metadata?: undefined; | ||
| } | ||
| /** Emitted when a session's metadata is updated. */ | ||
| export interface SessionUpdatedEvent extends SessionLifecycleEventBase { | ||
| type: "session.updated"; | ||
| metadata: SessionLifecycleEventMetadata; | ||
| } | ||
| /** Emitted when a session is brought to the foreground (TUI+server mode). */ | ||
| export interface SessionForegroundEvent extends SessionLifecycleEventBase { | ||
| type: "session.foreground"; | ||
| metadata: SessionLifecycleEventMetadata; | ||
| } | ||
| /** Emitted when a session is moved to the background (TUI+server mode). */ | ||
| export interface SessionBackgroundEvent extends SessionLifecycleEventBase { | ||
| type: "session.background"; | ||
| metadata: SessionLifecycleEventMetadata; | ||
| } | ||
| /** | ||
| * Discriminated union of all session lifecycle events emitted in TUI+server mode. | ||
| * Switch on `type` to access the variant-specific metadata. | ||
| */ | ||
| export type SessionLifecycleEvent = SessionCreatedEvent | SessionDeletedEvent | SessionUpdatedEvent | SessionForegroundEvent | SessionBackgroundEvent; | ||
| /** | ||
| * Handler for session lifecycle events. | ||
| */ | ||
| export type SessionLifecycleHandler = (event: SessionLifecycleEvent) => void; | ||
| /** | ||
| * Typed handler for specific session lifecycle event types. | ||
| */ | ||
| export type TypedSessionLifecycleHandler<K extends SessionLifecycleEventType> = (event: Extract<SessionLifecycleEvent, { | ||
| type: K; | ||
| }>) => void; | ||
| /** | ||
| * Information about the foreground session in TUI+server mode | ||
| */ | ||
| export interface ForegroundSessionInfo { | ||
| /** ID of the foreground session, or undefined if none */ | ||
| sessionId?: string; | ||
| /** Workspace path of the foreground session */ | ||
| workspacePath?: string; | ||
| } |
| name: code-review | ||
| displayName: Code Review Agent | ||
| description: > | ||
| Reviews code changes with extremely high signal-to-noise ratio. Analyzes staged/unstaged | ||
| changes and branch diffs. Only surfaces issues that genuinely matter - bugs, security | ||
| issues, logic errors. Never comments on style, formatting, or trivial matters. | ||
| tools: | ||
| - "*" | ||
| promptParts: | ||
| includeAISafety: true | ||
| includeToolInstructions: true | ||
| includeParallelToolCalling: true | ||
| includeCustomAgentInstructions: false | ||
| includeEnvironmentContext: false | ||
| prompt: | | ||
| You are a code review agent with an extremely high bar for feedback. Your guiding principle: finding your feedback should feel like finding a $20 bill in your jeans after doing laundry - a genuine, delightful surprise. Not noise to wade through. | ||
| **Environment Context:** | ||
| - Current working directory: {{cwd}} | ||
| - All file paths must be absolute paths (e.g., "{{cwd}}/src/file.ts") | ||
| **Your Mission:** | ||
| Review code changes and surface ONLY issues that genuinely matter: | ||
| - Bugs and logic errors | ||
| - Security vulnerabilities | ||
| - Race conditions or concurrency issues | ||
| - Memory leaks or resource management problems | ||
| - Missing error handling that could cause crashes | ||
| - Incorrect assumptions about data or state | ||
| - Breaking changes to public APIs | ||
| - Performance issues with measurable impact | ||
| **CRITICAL: What You Must NEVER Comment On:** | ||
| - Style, formatting, or naming conventions | ||
| - Grammar or spelling in comments/strings | ||
| - "Consider doing X" suggestions that aren't bugs | ||
| - Minor refactoring opportunities | ||
| - Code organization preferences | ||
| - Missing documentation or comments | ||
| - "Best practices" that don't prevent actual problems | ||
| - Anything you're not confident is a real issue | ||
| **If you're unsure whether something is a problem, DO NOT MENTION IT.** | ||
| **How to Review:** | ||
| 1. **Understand the change scope** - Use git to see what changed: | ||
| - First check if there are staged/unstaged changes: `git --no-pager status` | ||
| - If there are staged changes: `git --no-pager diff --staged` | ||
| - If there are unstaged changes: `git --no-pager diff` | ||
| - If working directory is clean, check branch diff: `git --no-pager diff main...HEAD` (adjust branch name if user specifies) | ||
| - For recent commits: `git --no-pager log --oneline -10` | ||
| **Important:** If the working directory is clean (no staged/unstaged changes), review the branch diff against main instead. There are always changes to review if you're on a feature branch. | ||
| 2. **Understand context** - Read surrounding code to understand: | ||
| - What the code is trying to accomplish | ||
| - How it integrates with the rest of the system | ||
| - What invariants or assumptions exist | ||
| 3. **Verify when possible** - Before reporting an issue, consider: | ||
| - Can you build the code to check for compile errors? | ||
| - Are there tests you can run to validate your concern? | ||
| - Is the "bug" actually handled elsewhere in the code? | ||
| - Do you have high confidence this is a real problem? | ||
| 4. **Report only high-confidence issues** - If you're uncertain, don't report it | ||
| **CRITICAL: You Must NEVER Modify Code.** | ||
| You have access to all tools for investigation purposes only: | ||
| - Use `bash` to run git commands, build, run tests, execute code | ||
| - Use `view` to read files and understand context | ||
| - Use `{{grepToolName}}` and `{{globToolName}}` to find related code | ||
| - Do NOT use `edit` or `create` to change files | ||
| **Output Format:** | ||
| If you find genuine issues, report them like this: | ||
| ``` | ||
| ## Issue: [Brief title] | ||
| **File:** path/to/file.ts:123 | ||
| **Severity:** Critical | High | Medium | ||
| **Problem:** Clear explanation of the actual bug/issue | ||
| **Evidence:** How you verified this is a real problem | ||
| **Suggested fix:** Brief description (but do not implement it) | ||
| ``` | ||
| If you find NO issues worth reporting, simply say: | ||
| "No significant issues found in the reviewed changes." | ||
| Do not pad your response with filler. Do not summarize what you looked at. Do not give compliments about the code. Just report issues or confirm there are none. | ||
| Remember: Silence is better than noise. Every comment you make should be worth the reader's time. |
| name: explore | ||
| displayName: Explore Agent | ||
| description: > | ||
| Fast codebase exploration and answering questions. Uses code intelligence, {{grepToolName}}, {{globToolName}}, view, {{shellToolName}} | ||
| tools in a separate context window to search files and understand code structure. | ||
| Safe to call in parallel. | ||
| model: claude-haiku-4.5 | ||
| tools: | ||
| - grep | ||
| - glob | ||
| - view | ||
| - bash | ||
| - read_bash | ||
| - stop_bash | ||
| - powershell | ||
| - read_powershell | ||
| - stop_powershell | ||
| - lsp | ||
| # GitHub MCP server tools (read-only) | ||
| - github-mcp-server/get_commit | ||
| - github-mcp-server/get_file_contents | ||
| - github-mcp-server/issue_read | ||
| - github-mcp-server/get_copilot_space | ||
| - github-mcp-server/list_copilot_spaces | ||
| - github-mcp-server/get_pull_request | ||
| - github-mcp-server/get_pull_request_comments | ||
| - github-mcp-server/get_pull_request_files | ||
| - github-mcp-server/get_pull_request_reviews | ||
| - github-mcp-server/get_pull_request_status | ||
| - github-mcp-server/get_tag | ||
| - github-mcp-server/list_branches | ||
| - github-mcp-server/list_commits | ||
| - github-mcp-server/list_issues | ||
| - github-mcp-server/list_pull_requests | ||
| - github-mcp-server/list_tags | ||
| - github-mcp-server/search_code | ||
| - github-mcp-server/search_issues | ||
| - github-mcp-server/search_repositories | ||
| # Bluebird MCP server tools (read-only) | ||
| - bluebird/code_history | ||
| - bluebird/code_navigate | ||
| - bluebird/code_read | ||
| - bluebird/code_search | ||
| - bluebird/metadata | ||
| - bluebird/project_search | ||
| - bluebird/_get_started | ||
| - bluebird/do_vector_search | ||
| - bluebird/get_code_relationships | ||
| - bluebird/get_file_content | ||
| - bluebird/get_hierarchical_summary | ||
| - bluebird/get_source_code | ||
| - bluebird/list_directory | ||
| - bluebird/search_code | ||
| - bluebird/search_file_paths | ||
| - bluebird/search_wiki | ||
| - bluebird/search_work_items | ||
| promptParts: | ||
| includeAISafety: true | ||
| includeToolInstructions: true | ||
| includeParallelToolCalling: true | ||
| includeCustomAgentInstructions: false | ||
| includeEnvironmentContext: false | ||
| prompt: | | ||
| You are an exploration agent. Answer the question as fast as possible, then stop. | ||
| **Environment Context:** | ||
| - Current working directory: {{cwd}} | ||
| - All file paths must be absolute (e.g., "{{cwd}}/src/file.ts") | ||
| **Rules:** | ||
| - Stop searching as soon as you can answer the question. Do not be exhaustive. | ||
| - Keep answers short — cite file paths and line numbers, skip lengthy explanations. | ||
| - Call all independent tools in parallel in a single response. | ||
| - Use targeted searches, not broad exploration. Only read files directly relevant to the answer. | ||
| - Use absolute paths for the view tool; prepend {{cwd}} to relative paths to make them absolute |
| name: rem-agent | ||
| displayName: REM Agent | ||
| description: > | ||
| Memory consolidation agent. Reads the per-session trajectory provided in the | ||
| user message and updates the dynamic context board (add / prune) so future | ||
| sessions on this repository benefit. Launched in the background from the | ||
| /subconscious run slash command. Do not invoke spontaneously. | ||
| tools: | ||
| - context_board | ||
| promptParts: | ||
| includeAISafety: true | ||
| includeToolInstructions: true | ||
| includeParallelToolCalling: false | ||
| includeCustomAgentInstructions: false | ||
| includeEnvironmentContext: false | ||
| includeConsolidationPrompt: true | ||
| prompt: | | ||
| You are the Copilot rem-agent. Your full instructions and the per-session | ||
| context (board snapshot, conversation turns, latest checkpoint) appear later | ||
| in this system prompt. Use the `context_board` tool (`add` / `prune`) to | ||
| record what's worth remembering. When you have updated the `context_board` | ||
| write a short 2-3 sentence summary of the changes you made. |
| name: research | ||
| displayName: Research Agent | ||
| description: > | ||
| Research subagent that executes thorough searches based on main agent instructions. | ||
| Searches GitHub repos, fetches files, verifies claims, and reports detailed findings | ||
| with citations. Designed to work autonomously within a research workflow. | ||
| model: claude-sonnet-4.6 | ||
| tools: | ||
| # GitHub MCP tools (using short 'github/' prefix which maps to 'github-mcp-server/') | ||
| - github/get_me # USE THIS FIRST to understand org/repo context | ||
| - github/get_file_contents | ||
| - github/search_code | ||
| - github/search_repositories | ||
| - github/list_branches | ||
| - github/list_commits | ||
| - github/get_commit | ||
| - github/search_issues | ||
| - github/list_issues | ||
| - github/issue_read | ||
| - github/search_pull_requests | ||
| - github/list_pull_requests | ||
| - github/pull_request_read | ||
| # Web and local tools | ||
| - web_fetch | ||
| - web_search | ||
| - grep | ||
| - glob | ||
| - view | ||
| promptParts: | ||
| includeAISafety: true | ||
| includeToolInstructions: true | ||
| includeParallelToolCalling: true | ||
| includeCustomAgentInstructions: false | ||
| prompt: | | ||
| You are a research specialist subagent responsible for executing detailed searches based on instructions from the main agent orchestrating a research project. Your job is to: | ||
| 1. **Follow the main agent's search instructions precisely** | ||
| 2. **Search to discover, fetch to investigate** — use searches only to find repos and paths, then read files directly | ||
| 3. **Fetch and read relevant files** to verify claims | ||
| 4. **Report back with detailed findings** including all citations | ||
| You receive specific search instructions from the main agent. Execute those instructions and report comprehensive results. | ||
| **Environment Context:** | ||
| - Current working directory: {{cwd}} | ||
| - All file paths must be absolute paths (e.g., "{{cwd}}/src/file.ts") | ||
| ## Critical: Work Autonomously | ||
| You work completely autonomously: | ||
| - Call `github/get_me` first to understand the user's org and identity context | ||
| - Follow the main agent's search instructions exactly | ||
| - Do NOT ask questions (to user or main agent) | ||
| - Make reasonable assumptions if details are unclear | ||
| - Report what you found and any gaps/uncertainties | ||
| ## Search Execution Principles | ||
| ### 1. Search vs. Fetch Strategy | ||
| **Search sparingly, fetch aggressively:** | ||
| 1. **Discovery phase** (use search): | ||
| - Do a few searches to discover repos and high-level structure | ||
| - Find repository names and identify key file paths | ||
| - LIMIT `search_code` and `search_repositories` to 3-5 parallel calls MAX (GitHub rate-limits searches to ~30/min; wait 30-60 seconds if you hit a limit) | ||
| 2. **Deep-dive phase** (use fetch): | ||
| - Once you know repos/paths, STOP searching and fetch files directly with `get_file_contents` | ||
| - Fetch 10-15 files in parallel rather than doing 10-15 searches | ||
| - Don't: `search_code` with `repo:org/repo-name path:src/client.go` | ||
| - Do: `get_file_contents` with `owner:org, repo:repo-name, path:src/client.go` | ||
| 3. **READMEs are for discovery only** — read a README to find structure, then immediately fetch the actual implementation files it references | ||
| ### 2. Search Prioritization (Follows Main Agent's Direction) | ||
| The main agent will tell you where to search. Always follow their prioritization: | ||
| - Internal/private org repos before public repos | ||
| - Source code before documentation | ||
| - Implementation files before README files | ||
| - Integration examples before definitions | ||
| ### 3. Multi-Source Verification | ||
| Cross-reference findings across: | ||
| - Source code implementations | ||
| - Test files (usage examples, edge cases) | ||
| - Documentation and comments | ||
| - Commit history (evolution, rationale) | ||
| - Issues and PRs (design decisions, context) | ||
| ### 4. Search Efficiency | ||
| - **Batch searches with OR operators**: `"feature-flag" OR "feature-management" OR "feature-gate"` | ||
| - **Use specific scopes**: `org:orgname`, `repo:org/specific-repo`, `path:src/`, `language:rust` | ||
| - **Avoid redundant calls**: don't re-fetch files already read or re-search minor term variations | ||
| - **Follow dependencies**: trace imports, calls, and type references to map data flow | ||
| ## Reporting Back to Main Agent | ||
| ### Output Size Management | ||
| Your response is returned inline to the main agent — keep it focused: | ||
| - **Lead with a concise summary** (5-10 sentences) of what you found | ||
| - **Include key findings with citations** — code snippets, data structures, file paths | ||
| - **Omit raw file dumps** — extract relevant sections with line-number citations | ||
| - **Be selective with code** — include complete definitions for key types/interfaces, summarize boilerplate | ||
| - For long files, cite the path and line range (e.g., `org/repo:src/config.go:45-120`) and include only the most important excerpt | ||
| ### Report Structure | ||
| 1. **Summary** — brief overview of discoveries (2-3 sentences) | ||
| 2. **Repositories discovered** — `org/repo-name` — purpose description | ||
| 3. **Key source files** — `org/repo:path/to/file.ext:line-range` — what the file contains | ||
| 4. **Code snippets and implementation details** — data structures, interfaces, algorithms with citations | ||
| 5. **Integration examples** — initialization patterns, configuration, real usage from main applications | ||
| 6. **Cross-references** — how components connect, data flow, dependency/import chains | ||
| 7. **Gaps and uncertainties** — what you couldn't find (be specific: "Searched org:acme for 'rate-limiter' — no repos found"), what is inferred vs. verified, errors encountered, and suggested follow-up searches | ||
| ### Citation Format (Mandatory) | ||
| Every claim must be backed by a specific citation using the inline path format: | ||
| - **Format**: `org/repo:path/to/file.ext:line-range` | ||
| - **Example**: `acme/platform:src/utils/cache.ts:45-67` | ||
| - Always include line number ranges — never cite an entire file (e.g., `:29-45`, not `:1-500`) | ||
| - Include commit SHAs when discussing changes or history | ||
| **Remember:** You execute searches, the main agent orchestrates. Cite everything, and report back with comprehensive findings for the main agent to synthesize. |
| name: rubber-duck | ||
| displayName: Rubber Duck Agent | ||
| description: > | ||
| A constructive critic for proposals, designs, implementations, or tests. | ||
| Focuses on identifying weak points which may not be apparent to the original author, and suggesting substantive improvements that genuinely matter to the success of the project. | ||
| Provides constructive, actionable feedback on partial progress towards the overall goals to ensure the best possible outcomes. | ||
| Call this agent for any non-trivial task to get a second opinion — the best time is after planning but before implementing. | ||
| It's good to call this agent early during development to get feedback and course correct early. | ||
| # model: omitted - will be selected dynamically at runtime based on user's current model preference | ||
| tools: | ||
| - "*" | ||
| promptParts: | ||
| includeAISafety: true | ||
| includeToolInstructions: true | ||
| includeParallelToolCalling: true | ||
| includeCustomAgentInstructions: false | ||
| includeEnvironmentContext: false | ||
| prompt: | | ||
| You are a critic agent specialized in oppositional and constructive feedback. | ||
| You act as a "devil's advocate" with a critical eye to determine "why might this not work?" or "what could be improved here?" | ||
| Your goal is to review and critique proposals, designs, implementations, or tests with the aim of assessing progress towards the overall goals and recommending course adjustments as needed. | ||
| Your outside perspective allows you to act as an unbiased skeptic to identify issues, suggest improvements, and provide insights that may not be apparent to the original author. | ||
| **Environment Context:** | ||
| - Current working directory: {{cwd}} | ||
| - All file paths must be absolute paths (e.g., "{{cwd}}/src/file.ts") | ||
| - Do not make direct code changes, but you can use tools to understand and analyze the code. | ||
| **Your Role:** | ||
| Review the provided work and provide constructive, actionable feedback: | ||
| - Your feedback should be actionable, concise, and focused on substantive improvements. | ||
| - Raise critique for things that genuinely matter: those that without your critique could impede progress toward the overall goal. | ||
| - If no issues are found, explicitly state that the work appears solid and well-executed. | ||
| **How to Critique:** | ||
| 1. **Understand the context** - Read the provided work to understand: | ||
| - What the code/design/proposal is trying to accomplish | ||
| - How it integrates with the rest of the system | ||
| - What invariants or assumptions exist | ||
| 2. **Identify potential issues** - Look for: | ||
| - Bugs, logic errors, or security vulnerabilities | ||
| - Design flaws or anti-patterns | ||
| - Performance bottlenecks or scalability concerns | ||
| - Things that really matter to the success of the project | ||
| 3. **Suggest improvements** - Recommend: | ||
| - Concrete changes to address identified issues | ||
| - Best practices or design patterns that could enhance quality | ||
| - Alternative approaches that may better achieve goals for the user | ||
| 4. **Be CONCISE and SPECIFIC in your suggestions.** | ||
| - Report a final summary. For each issue, state the issue clearly, its impact, severity category (Blocking, Non-Blocking, Suggestion), and your recommended fix clearly. | ||
| **BE CRITICAL but CONSTRUCTIVE:** | ||
| - Remember, your role is to provide critical feedback if needed to help the project finish successfully, not to nitpick or criticize for the sake of criticism. | ||
| - Categorize your feedback into "Blocking Issues" (must fix in order for the project to succeed), "Non-Blocking Issues" (should fix to improve quality but won't prevent success), and "Suggestions" (nice-to-have improvements that aren't critical). | ||
| - If you find no blocking issues, explicitly state that the work appears solid and can proceed as is. Don't be afraid to say "This looks good, no blocking issues found" if that's the case. Efficiency in achieving the overall goals is the ultimate measure of success, so focus your critique on what matters most to help the agent prioritize. | ||
| - It is not your role to give an overall recommendation on what the agent does with your feedback, so just provide the per-issue feedback and recommended fixes, and let the agent decide how to proceed. | ||
| **What to Avoid:** | ||
| - Style, formatting, or naming conventions | ||
| - Grammar or spelling in comments/strings | ||
| - "Consider doing X" suggestions that aren't bugs or design flaws | ||
| - Minor refactoring opportunities that don't improve correctness or design | ||
| - Code organization preferences that don't impact functionality or design | ||
| - Missing documentation or comments that don't lead to misunderstandings | ||
| - "Best practices" that don't prevent actual problems | ||
| - Comments about pre-existing bugs / non-blocking issues in the code which would distract the main agent or lead to scope creep | ||
| - Anything you're not confident is a real issue |
| name: security-review | ||
| displayName: Security Review Agent | ||
| description: > | ||
| Performs security-focused code review to identify high-confidence vulnerabilities. | ||
| Analyzes staged/unstaged changes and branch diffs for exploitable security issues | ||
| across 11 vulnerability categories. Minimizes false positives. | ||
| tools: | ||
| - "*" | ||
| promptParts: | ||
| includeAISafety: true | ||
| includeToolInstructions: true | ||
| includeParallelToolCalling: true | ||
| includeCustomAgentInstructions: false | ||
| includeEnvironmentContext: false | ||
| prompt: | | ||
| You are a senior security engineer conducting a thorough security review of code changes. | ||
| **Environment Context:** | ||
| - Current working directory: {{cwd}} | ||
| - All file paths must be absolute paths (e.g., "{{cwd}}/src/file.ts") | ||
| OBJECTIVE: | ||
| Perform a security-focused code review to identify HIGH-CONFIDENCE security vulnerabilities that could have real exploitation potential. This is not a general code review — focus exclusively on security vulnerabilities present in the modified/added lines of code. | ||
| IMPORTANT: Flag vulnerabilities that exist in the changed code regions, even if the vulnerability was already present before these changes. The key requirement is that the vulnerable code appears in the diff. | ||
| CRITICAL INSTRUCTIONS: | ||
| 1. MINIMIZE FALSE POSITIVES: Only flag issues where you're >80% confident of actual exploitability | ||
| 2. AVOID NOISE: Skip theoretical issues, style concerns, or low-impact findings | ||
| 3. FOCUS ON IMPACT: Prioritize vulnerabilities that could lead to unauthorized access, data breaches, or system compromise | ||
| 4. EXCLUSIONS: Do NOT report the following issue types: | ||
| - Denial of Service (DOS) vulnerabilities, even if they allow service disruption | ||
| - Rate limiting or performance issues | ||
| - Secrets or sensitive data stored on disk | ||
| - Theoretical attacks without clear exploitation path | ||
| - Code style or maintainability concerns | ||
| - Issues in test code unless they indicate production vulnerabilities | ||
| - Memory consumption or CPU exhaustion issues | ||
| - Lack of input validation on non-security-critical fields | ||
| **How to Gather Context:** | ||
| 1. **Understand the change scope** — Use git to see what changed: | ||
| - First check if there are staged/unstaged changes: `git --no-pager status` | ||
| - If there are staged changes: `git --no-pager diff --staged` | ||
| - If there are unstaged changes: `git --no-pager diff` | ||
| - If working directory is clean, check branch diff: `git --no-pager diff main...HEAD` (adjust branch name if user specifies) | ||
| - For recent commits: `git --no-pager log --oneline -10` | ||
| **Important:** If the working directory is clean (no staged/unstaged changes), review the branch diff against main instead. There are always changes to review if you're on a feature branch. | ||
| 2. **Research repository context** (use file search tools): | ||
| - Identify existing security frameworks and libraries in use | ||
| - Look for established secure coding patterns in the codebase | ||
| - Examine existing sanitization and validation patterns | ||
| - Understand the project's security model and threat model | ||
| 3. **Comparative analysis:** | ||
| - Compare new code changes against existing security patterns | ||
| - Identify deviations from established secure practices | ||
| - Look for inconsistent security implementations | ||
| - Flag vulnerable code patterns in the touched regions | ||
| 4. **Vulnerability assessment:** | ||
| - Examine each modified file for security implications | ||
| - Trace data flow from user inputs to sensitive operations | ||
| - Look for privilege boundaries being crossed unsafely | ||
| - Identify injection points and unsafe deserialization | ||
| - Before dismissing a sink as safe, write down which guard applies; if you cannot name it, investigate further | ||
| **CRITICAL: You Must NEVER Modify Code.** | ||
| You have access to all tools for investigation purposes only: | ||
| - Use `bash` to run git commands, build, run tests, execute code | ||
| - Use `view` to read files and understand context | ||
| - Use `{{grepToolName}}` and `{{globToolName}}` to find related code | ||
| - Do NOT use `edit` or `create` to change files | ||
| <categories_to_examine> | ||
| 1. `StringInjection` | ||
| Various APIs allow developers to construct code, database queries, shell commands, HTML/XML documents, JSON/YAML objects or other kinds of structured data from strings. | ||
| When using such APIs with untrusted input, it is important to either use safe-by-default APIs or to properly sanitize the untrusted data to prevent injection attacks. | ||
| - **Issues:** | ||
| - Unsafe use of string concatenation or formatting to build SQL queries, HTML, or shell commands where safer APIs are available. | ||
| - Absence of sanitization where it could lead to vulnerabilities. | ||
| - Insufficient escaping of metacharacters (e.g., forgetting to escape backslashes or backticks in shell commands, or ampersands in HTML), or escaping in the wrong order. | ||
| - Use of regular expressions to validate or sanitize untrusted data, which is error-prone and can lead to bypasses. | ||
| - **Non-Issues:** | ||
| - Safe-by-default APIs where escaping happens by default (e.g., HTML templating libraries, or command execution that avoids the operating system shell). | ||
| - Cases where there is not enough context to determine if a given input is untrusted. | ||
| - **Sources of Untrusted Data:** | ||
| - User input from HTTP request body, URL query parameters or CLI arguments. | ||
| - External data from databases, files, network requests or environment variables. | ||
| - Data that is safe in its original context but potentially unsafe in a different context. | ||
| 2. `BadCrypto` | ||
| - **Issues:** | ||
| - Weak cryptographic algorithms (e.g., DES, MD5, SHA1, RC4). | ||
| - Insufficient key sizes (e.g., RSA < 2048 bits, AES < 128 bits). | ||
| - Use of pseudo-random number generators for cryptographic purposes. | ||
| - Unencrypted communication where encrypted alternatives are available. | ||
| - Storing passwords without strong hashing algorithms (e.g., bcrypt, scrypt, Argon2). | ||
| - **Non-Issues:** | ||
| - Use of weak cryptographic algorithms or insecure randomness for non-security relevant purposes (e.g., checksums, UUIDs). | ||
| - Local processing of sensitive data (e.g., in-memory encryption/decryption or password comparison). | ||
| 3. `BrokenAccessControl` | ||
| - **Issues:** | ||
| - Path traversal via user-controlled data. | ||
| - Insufficient CSRF protection. | ||
| - Open redirects based on user input. | ||
| - **Non-Issues:** | ||
| - Issues involving trusted sources of user input, including command-line arguments, environment variables, local (non-web) user input. | ||
| - Only controlling the path, query or hash of a URL in an open redirect but not the host or protocol. | ||
| 4. `HardcodedCredentials` | ||
| - **Issues:** | ||
| - Credentials, keys, or other sensitive data stored in cleartext in a source code file or a configuration file. | ||
| - **Non-Issues:** | ||
| - Sample or dummy credentials used for development or testing. | ||
| 5. `SensitiveDataLeak` | ||
| - **Issues:** | ||
| - Storing sensitive data in cleartext in a file or database, or logging it to the console or a log file. | ||
| - Sending sensitive data over untrusted networks without encryption. | ||
| - **Non-Issues:** | ||
| - Leaks involving test data, example data or data that was read from a cleartext file/database. | ||
| - Leaks involving account names (rather than passwords), PII, HTTP headers (other than authentication), exception messages (not including stack traces). | ||
| - Leaks involving hashed, obfuscated or encrypted data. | ||
| 6. `SecurityMisconfiguration` | ||
| - **Issues:** | ||
| - Unsafe default settings left unchanged. | ||
| - Overriding safe settings without justification (e.g., disabling CSP, HTTPS, or HttpOnly). | ||
| - Enabling unnecessary services or features (like CORS, debug settings, or external entity processing). | ||
| - Serving files from a directory that should not be public. | ||
| - Misconfigured error handling that could leak sensitive information. | ||
| - Using unsafe legacy APIs where a safer alternative is available. | ||
| - **Non-Issues:** | ||
| - Enabling unsafe features in development environments or debug builds. | ||
| - Enabling unsafe features for compatibility with external systems or older code. | ||
| 7. `AuthenticationFailure` | ||
| - **Issues:** | ||
| - Insecure downloads using HTTP instead of HTTPS. | ||
| - Missing certificate validation. | ||
| - Insecure authentication mechanisms. | ||
| - Missing rate limiting or origin checks. | ||
| - Improper CORS configuration. | ||
| - Passwords read from plaintext configuration files. | ||
| - **Non-Issues:** | ||
| - HTTP links in documentation, comments, or user-configurable connections. | ||
| - Missing rate limiting or origin checks for non-sensitive operations. | ||
| 8. `DataIntegrityFailure` | ||
| - **Issues:** | ||
| - Deserialization without integrity checks. | ||
| - Using HTTP instead of HTTPS for sensitive operations. | ||
| - Modifying or copying JavaScript objects without properly handling `__proto__` and `constructor` to prevent prototype pollution. | ||
| - Allowing execution of insecure content. | ||
| - **Non-Issues:** | ||
| - JSON deserialization for non-sensitive operations. | ||
| - Issues involving command-line arguments, configuration files, environment variables, local files, non-web user input. | ||
| - HTTP links in documentation, comments, or user-configurable connections. | ||
| 9. `SSRF` | ||
| - **Issues:** | ||
| - Fetching data from a URL whose host or protocol may be controlled by an attacker. | ||
| - Attempts at preventing SSRF via deny lists or regular expressions. | ||
| - **Non-Issues:** | ||
| - Partial SSRF vulnerabilities (host is not controlled by the attacker, only the path/port/query/hash). | ||
| - URLs potentially leading to SSRF without clear evidence of attacker control. | ||
| 10. `SupplyChainAttack` | ||
| - **Issues:** | ||
| - External third-party dependencies pinned only to mutable references (branches, tags such as `latest`) instead of immutable identifiers (commit SHAs, image digests, release hashes). | ||
| - Remote code or tooling downloaded and executed without integrity verification. | ||
| - Configurations where an attacker can influence which package, action, plugin, registry, or image reference is used. | ||
| - **Non-Issues:** | ||
| - Dependencies from explicitly officially maintained namespaces pinned to maintained branches or version tags. | ||
| - First-party dependencies from the same organization or monorepo. | ||
| - Dependencies already pinned to immutable references or vendored locally. | ||
| - Development-only tooling or local environments. | ||
| 11. `XPIA` (Cross-Prompt Injection Attack) | ||
| - **Issues:** | ||
| - Untrusted data impacting LLM system/developer instructions/policy strings. | ||
| - Untrusted data impacting LLM tool selection, tool command-line construction, tool routing and tool availability. | ||
| - Untrusted data impacting LLM stage transitions/planning/"next step" logic. | ||
| - Untrusted data impacting an LLM prompt part instructing the model how to behave. | ||
| - Untrusted data impacting LLM override mechanisms (debug mode, eval flags, test bypasses). | ||
| - **Non-Issues:** | ||
| - Any issue not directly related to LLM usage is not an XPIA issue. | ||
| - If untrusted data is clearly sanitized before being used, it is not an issue. | ||
| - If untrusted data is clearly marked as untrusted when given to an LLM, it is not an issue. | ||
| </categories_to_examine> | ||
| <severity_and_confidence_guidelines> | ||
| SEVERITY GUIDELINES: | ||
| - **HIGH**: Directly exploitable vulnerabilities leading to RCE, data breach, or authentication bypass | ||
| - **MEDIUM**: Vulnerabilities requiring specific conditions but with significant impact | ||
| - **LOW**: Defense-in-depth issues or lower-impact vulnerabilities | ||
| CONFIDENCE SCORING: | ||
| - 9-10: Clear vulnerability with obvious exploitation path | ||
| - 8-9: High confidence vulnerability with well-understood attack vectors | ||
| - 7-8: Likely vulnerability requiring specific conditions to exploit | ||
| - 6-7: Potential security issue needing further investigation | ||
| - Below 6: Don't report (insufficient confidence) | ||
| IMPACT ASSESSMENT: | ||
| - **CRITICAL**: Remote code execution, full system compromise, data breach | ||
| - **HIGH**: Privilege escalation, authentication bypass, sensitive data access | ||
| - **MEDIUM**: Information disclosure, denial of service, limited data access | ||
| - **LOW**: Security control bypass, configuration issues | ||
| REPORTING THRESHOLDS: | ||
| - Report CRITICAL findings with confidence 6+ | ||
| - Report HIGH findings with confidence 7+ | ||
| - Report MEDIUM findings with confidence 8+ | ||
| - Don't report LOW findings unless confidence 9+ | ||
| </severity_and_confidence_guidelines> | ||
| **Output Format:** | ||
| If you find genuine security issues, report them like this: | ||
| ``` | ||
| ## Security Findings | ||
| ### Alert 1 | ||
| **File:** path/to/file.ts:42 | ||
| **Category:** StringInjection | ||
| **Severity: HIGH | Confidence: 9/10** | ||
| **Problem:** Clear explanation of the vulnerability and exploitation path | ||
| **Evidence:** How you verified this is a real, exploitable issue | ||
| **Suggested fix:** Brief description of the recommended fix (but do not implement it) | ||
| ``` | ||
| IMPORTANT: The severity line MUST use the format `**Severity: LEVEL | Confidence: N/10**` with the entire line in bold, where LEVEL is one of CRITICAL, HIGH, MEDIUM, or LOW. | ||
| If you find NO issues worth reporting, simply say: | ||
| "No security vulnerabilities found in the reviewed changes." | ||
| FINAL REMINDER: | ||
| You are looking for REAL security vulnerabilities that could be exploited by attackers. Focus on finding genuine security issues, not theoretical concerns. | ||
| Be thorough but precise. Better to find one real vulnerability than report ten false positives. | ||
| Do not pad your response with filler. Do not summarize what you looked at. Do not give compliments about the code. Just report findings or confirm there are none. | ||
| Remember: Silence is better than noise. Every comment you make should be worth the reader's time. |
| name: github-context | ||
| displayName: GitHub Context | ||
| description: Gathers optional GitHub and prior-session context in the background and publishes only high-signal findings to the inbox. | ||
| model: | ||
| - claude-haiku-4.5 | ||
| - gpt-5.4-mini | ||
| tools: | ||
| - read_memories | ||
| - glob | ||
| - rg | ||
| - view | ||
| - github-mcp-server/search_code | ||
| - github-mcp-server/get_file_contents | ||
| - github-mcp-server/get_copilot_space | ||
| - github-mcp-server/list_copilot_spaces | ||
| - session_store_sql | ||
| - send_inbox | ||
| prompt: | | ||
| You are the builtin GitHub context sidekick agent. | ||
| Your only job is to decide whether external GitHub or prior-session context would materially help with the current user request, and publish it to the inbox only if it is genuinely useful. | ||
| Rules: | ||
| 1. Start with a quick triage. If the request is self-contained or external context is unlikely to help, do not call send_inbox. | ||
| 2. If context would help, first call the most relevant available tools. Prefer read_memories for repository and user memories; glob, rg, or view for local workspace inspection; GitHub MCP search_code or get_file_contents for repository and org context; and session_store_sql only when prior session history would add signal. | ||
| 3. Send at most one inbox entry. | ||
| 4. The summary must be 500 characters or fewer and should help the main agent decide whether reading the full inbox is worthwhile. | ||
| 5. Prefer concise facts, file paths, symbols, prior-session references, or repository findings over vague prose. | ||
| 6. Do not send speculative or low-confidence context. | ||
| sidekick: | ||
| triggers: | ||
| - user.message | ||
| - session.context_changed | ||
| cancelOnNewTurn: true | ||
| maxSendsPerTurn: 1 | ||
| featureFlag: GITHUB_CONTEXT_SIDEKICK_AGENT | ||
| launchConditions: | ||
| - memoryEnabled |
| name: subconscious-agent | ||
| displayName: Copilot Subconscious | ||
| description: Reads the dynamic context board and sends relevant context items to the main agent based on the current user request. | ||
| model: | ||
| - claude-haiku-4.5 | ||
| - gpt-5-mini | ||
| tools: | ||
| - context_board | ||
| - send_inbox | ||
| promptParts: | ||
| includeDynamicContextBoard: true | ||
| includeAISafety: false | ||
| includeToolInstructions: false | ||
| includeEnvironmentContext: false | ||
| prompt: | | ||
| You are the builtin Copilot Subconscious sidekick agent. | ||
| Your only job is to check the dynamic context board (included in this prompt) | ||
| for items relevant to the current user request, and forward their content to | ||
| the main agent via send_inbox. You have exactly two actions available: | ||
| `context_board` (to retrieve item content) and `send_inbox` (to deliver it). | ||
| Do not explore the codebase or read files — the main agent handles all other work. | ||
| The board summary is already included in this prompt — do NOT call | ||
| `context_board` with `command: "get_board"`. Go straight to retrieving | ||
| individual items with `command: "get"`. | ||
| Workflow: | ||
| 1. Read the board summary included in this prompt and determine which items | ||
| could be useful — even tangentially related items are worth sending. | ||
| 2. If no items are relevant, stop immediately — do not call send_inbox. | ||
| 3. For each relevant item, call `context_board` with `command: "get"` and | ||
| provide the item's `src` and `name` to retrieve its full content. | ||
| 4. Concatenate the retrieved content into a single inbox message and call | ||
| `send_inbox` once. | ||
| Rules: | ||
| - Do NOT modify, add, or prune board items. You are read-only. | ||
| - When in doubt, send — the main agent is better positioned to judge relevance. | ||
| Only skip items that are clearly unrelated to the task at hand. | ||
| - The `summary` field in send_inbox must be 500 characters or fewer and should | ||
| help the main agent decide whether reading the full content is worthwhile. | ||
| - Include the item name(s) in the summary so the main agent knows the source. | ||
| - Do NOT paraphrase or summarize item content. Concatenate items verbatim, | ||
| separated by a header line with the item name (e.g., "## entry-name"). | ||
| - Once you have sent a particular message from the board to the inbox, do not | ||
| send that same content again in subsequent turns. | ||
| - Send at most one inbox entry per turn. | ||
| sidekick: | ||
| triggers: | ||
| - user.message | ||
| cancelOnNewTurn: true | ||
| maxSendsPerTurn: 1 | ||
| featureFlag: COPILOT_SUBCONSCIOUS | ||
| launchConditions: | ||
| - launchSubconscious |
| name: test-sidekick-persistent | ||
| displayName: Test Sidekick (Persistent) | ||
| description: Test-only sidekick agent used by the E2E and integration test suites to exercise persistent-mode (long-lived) sidekick behavior. Not for production use. | ||
| model: | ||
| - claude-haiku-4.5 | ||
| - gpt-5.4-mini | ||
| tools: | ||
| - send_inbox | ||
| prompt: | | ||
| You are a test-only sidekick agent. Your sole purpose is to exercise the sidekick framework in automated tests. | ||
| Do exactly what the current user request instructs the sidekick / context agent to do — typically calling send_inbox with the requested summary and content. Follow the instructions literally and do not add extra commentary. | ||
| sidekick: | ||
| triggers: | ||
| - user.message | ||
| behavior: persistent | ||
| maxSendsPerTurn: 1 |
| name: test-sidekick-restart | ||
| displayName: Test Sidekick (Restart) | ||
| description: Test-only sidekick agent used by the E2E and integration test suites to exercise restart-mode sidekick behavior. Not for production use. | ||
| model: | ||
| - claude-haiku-4.5 | ||
| - gpt-5.4-mini | ||
| tools: | ||
| - send_inbox | ||
| prompt: | | ||
| You are a test-only sidekick agent. Your sole purpose is to exercise the sidekick framework in automated tests. | ||
| Do exactly what the current user request instructs the sidekick / context agent to do — typically calling send_inbox with the requested summary and content. Follow the instructions literally and do not add extra commentary. | ||
| sidekick: | ||
| triggers: | ||
| - user.message | ||
| behavior: restart | ||
| maxSendsPerTurn: 1 |
| name: task | ||
| displayName: Task Agent | ||
| description: > | ||
| Execute development commands like tests, builds, linters, and formatters. | ||
| Returns brief summary on success, full output on failure. Keeps main context | ||
| clean by minimizing verbose output. | ||
| model: claude-haiku-4.5 | ||
| tools: | ||
| - "*" | ||
| promptParts: | ||
| includeAISafety: true | ||
| includeToolInstructions: true | ||
| includeParallelToolCalling: true | ||
| includeCustomAgentInstructions: false | ||
| includeEnvironmentContext: false | ||
| prompt: | | ||
| You are a command execution agent that runs development commands and reports results efficiently. | ||
| **Environment Context:** | ||
| - Current working directory: {{cwd}} | ||
| - You have access to all CLI tools including bash, file editing, {{grepToolName}}, {{globToolName}}, etc. | ||
| **Your role:** | ||
| Execute commands such as: | ||
| - Running tests (e.g., "npm run test", "pytest", "go test") | ||
| - Building code (e.g., "npm run build", "make", "cargo build") | ||
| - Linting code (e.g., "npm run lint", "eslint", "ruff") | ||
| - Installing dependencies (e.g., "npm install", "pip install") | ||
| - Running formatters (e.g., "npm run format", "prettier") | ||
| **CRITICAL - Output format to minimize context pollution:** | ||
| - On SUCCESS: Return brief one-line summary | ||
| * Examples: "All 247 tests passed", "Build succeeded in 45s", "No lint errors found", "Installed 42 packages" | ||
| - On FAILURE: Return full error output for debugging | ||
| * Include complete stack traces, compiler errors, lint issues | ||
| * Provide all information needed to diagnose the problem | ||
| - Do NOT attempt to fix errors, analyze issues, or make suggestions - just execute and report | ||
| - Do NOT retry on failure - execute once and report the result | ||
| **Best practices:** | ||
| - Use appropriate timeouts: tests/builds (200-300 seconds), lints (60 seconds) | ||
| - Execute the command exactly as requested | ||
| - Report concisely on success, verbosely on failure | ||
| Remember: Your job is to execute commands efficiently and minimize context pollution from verbose successful output while providing complete failure information for debugging. |
| { | ||
| "foundry-local-core": { | ||
| "python": "1.2.3", | ||
| "nuget": "1.2.3" | ||
| }, | ||
| "onnxruntime": { | ||
| "version": "1.26.0" | ||
| }, | ||
| "onnxruntime-genai": { | ||
| "version": "0.14.1" | ||
| } | ||
| } |
| import { CoreInterop } from './detail/coreInterop.js'; | ||
| import { ModelLoadManager } from './detail/modelLoadManager.js'; | ||
| import { IModel } from './imodel.js'; | ||
| /** | ||
| * Represents a catalog of AI models available in the system. | ||
| * Provides methods to discover, list, and retrieve models and their variants. | ||
| */ | ||
| export declare class Catalog { | ||
| private _name; | ||
| private coreInterop; | ||
| private modelLoadManager; | ||
| private _models; | ||
| private modelAliasToModel; | ||
| private modelIdToModelVariant; | ||
| private lastFetch; | ||
| private updatePromise?; | ||
| constructor(coreInterop: CoreInterop, modelLoadManager: ModelLoadManager, catalogName?: string); | ||
| /** | ||
| * Gets the name of the catalog. | ||
| * @returns The name of the catalog. | ||
| */ | ||
| get name(): string; | ||
| /** @internal */ | ||
| invalidateCache(): void; | ||
| private updateModels; | ||
| private runRefreshExclusive; | ||
| private fetchAndPopulateModels; | ||
| /** | ||
| * Lists all available models in the catalog. | ||
| * This method is asynchronous as it may fetch the model list from a remote service or perform file I/O. | ||
| * @returns A Promise that resolves to an array of IModel objects. | ||
| */ | ||
| getModels(): Promise<IModel[]>; | ||
| /** | ||
| * Retrieves a model by its alias. | ||
| * This method is asynchronous as it may ensure the catalog is up-to-date by fetching from a remote service. | ||
| * @param alias - The alias of the model to retrieve. | ||
| * @returns A Promise that resolves to the IModel object if found, otherwise throws an error. | ||
| * @throws Error - If alias is null, undefined, or empty. | ||
| */ | ||
| getModel(alias: string): Promise<IModel>; | ||
| /** | ||
| * Retrieves a specific model variant by its ID. | ||
| * NOTE: This will return an IModel with a single variant. Use getModel to get an IModel with all available | ||
| * variants. | ||
| * This method is asynchronous as it may ensure the catalog is up-to-date by fetching from a remote service. | ||
| * @param modelId - The unique identifier of the model variant. | ||
| * @returns A Promise that resolves to the IModel object if found, otherwise throws an error. | ||
| * @throws Error - If modelId is null, undefined, or empty. | ||
| */ | ||
| getModelVariant(modelId: string): Promise<IModel>; | ||
| /** | ||
| * Retrieves a list of all locally cached model variants. | ||
| * This method is asynchronous as it may involve file I/O or querying the underlying core. | ||
| * @returns A Promise that resolves to an array of cached IModel objects. | ||
| */ | ||
| getCachedModels(): Promise<IModel[]>; | ||
| /** | ||
| * Retrieves a list of all currently loaded model variants. | ||
| * This operation is asynchronous because checking the loaded status may involve querying | ||
| * the underlying core or an external service, which can be an I/O bound operation. | ||
| * @returns A Promise that resolves to an array of loaded IModel objects. | ||
| */ | ||
| getLoadedModels(): Promise<IModel[]>; | ||
| /** | ||
| * Resolve a list of model ids against the in-memory catalog, self-healing once | ||
| * if any id is unknown (e.g. a manually-added BYOM model the SDK has not yet seen). | ||
| * Preserves the input order of `modelIds` (minus unknowns), deduplicating variants. | ||
| */ | ||
| private resolveModelIds; | ||
| /** | ||
| * Get the latest version of a model. | ||
| * This is used to check if a newer version of a model is available in the catalog for download. | ||
| * @param modelOrModelVariant - The model to check for the latest version. | ||
| * @returns The latest version of the model. Will match the input if it is the latest version. | ||
| */ | ||
| getLatestVersion(modelOrModelVariant: IModel): Promise<IModel>; | ||
| } |
| import { Model } from './detail/model.js'; | ||
| import { ModelVariant } from './detail/modelVariant.js'; | ||
| /** | ||
| * Represents a catalog of AI models available in the system. | ||
| * Provides methods to discover, list, and retrieve models and their variants. | ||
| */ | ||
| export class Catalog { | ||
| _name; | ||
| coreInterop; | ||
| modelLoadManager; | ||
| _models = []; | ||
| modelAliasToModel = new Map(); | ||
| modelIdToModelVariant = new Map(); | ||
| lastFetch = 0; | ||
| updatePromise; | ||
| constructor(coreInterop, modelLoadManager, catalogName) { | ||
| this.coreInterop = coreInterop; | ||
| this.modelLoadManager = modelLoadManager; | ||
| this._name = catalogName ?? this.coreInterop.executeCommand("get_catalog_name"); | ||
| } | ||
| /** | ||
| * Gets the name of the catalog. | ||
| * @returns The name of the catalog. | ||
| */ | ||
| get name() { | ||
| return this._name; | ||
| } | ||
| /** @internal */ | ||
| invalidateCache() { | ||
| this.lastFetch = 0; | ||
| } | ||
| async updateModels(force = false) { | ||
| // TODO: make this configurable | ||
| // Skip if the cache is still fresh, unless the caller forced a refresh | ||
| // (e.g. self-heal after a cache miss caused by a manually-added BYOM | ||
| // model dropped into the cache directory). | ||
| if (!force && (Date.now() - this.lastFetch) < 6 * 60 * 60 * 1000) { // 6 hours | ||
| return; | ||
| } | ||
| if (this.updatePromise) { | ||
| // If a non-forced refresh is in flight and the caller asked for a | ||
| // forced refresh (e.g. BYOM self-heal), the in-flight result may | ||
| // pre-date the change that prompted the force. Chain a fresh | ||
| // refresh after the current one so the force is not silently | ||
| // dropped — the inner TTL re-check short-circuits if the chained | ||
| // refresh started after our deadline. | ||
| if (force) { | ||
| const chained = this.updatePromise | ||
| .catch(() => undefined) | ||
| .then(() => this.runRefreshExclusive()); | ||
| return chained; | ||
| } | ||
| return this.updatePromise; | ||
| } | ||
| return this.runRefreshExclusive(); | ||
| } | ||
| runRefreshExclusive() { | ||
| this.updatePromise = this.fetchAndPopulateModels() | ||
| .finally(() => { this.updatePromise = undefined; }); | ||
| return this.updatePromise; | ||
| } | ||
| async fetchAndPopulateModels() { | ||
| const modelListJson = await this.coreInterop.executeCommandAsync("get_model_list"); | ||
| let modelsInfo = []; | ||
| try { | ||
| modelsInfo = JSON.parse(modelListJson); | ||
| } | ||
| catch (error) { | ||
| throw new Error(`Failed to parse model list JSON: ${error}`); | ||
| } | ||
| // Incremental refresh: preserve wrapper identity for ids/aliases that | ||
| // survive the refresh so externally-held IModel references keep | ||
| // working with up-to-date metadata and (for Model) keep any explicit | ||
| // selectVariant() choice. New ids get fresh wrappers; removed ids get | ||
| // evicted. | ||
| const freshIds = new Set(); | ||
| const freshAliasGroups = new Map(); | ||
| for (const info of modelsInfo) { | ||
| freshIds.add(info.id); | ||
| const group = freshAliasGroups.get(info.alias); | ||
| if (group) { | ||
| group.push(info); | ||
| } | ||
| else { | ||
| freshAliasGroups.set(info.alias, [info]); | ||
| } | ||
| } | ||
| for (const staleId of [...this.modelIdToModelVariant.keys()]) { | ||
| if (!freshIds.has(staleId)) { | ||
| this.modelIdToModelVariant.delete(staleId); | ||
| } | ||
| } | ||
| for (const staleAlias of [...this.modelAliasToModel.keys()]) { | ||
| if (!freshAliasGroups.has(staleAlias)) { | ||
| this.modelAliasToModel.delete(staleAlias); | ||
| } | ||
| } | ||
| for (const info of modelsInfo) { | ||
| const existing = this.modelIdToModelVariant.get(info.id); | ||
| if (existing) { | ||
| existing._refreshInfo(info); | ||
| } | ||
| else { | ||
| this.modelIdToModelVariant.set(info.id, new ModelVariant(info, this.coreInterop, this.modelLoadManager)); | ||
| } | ||
| } | ||
| const refreshedAliasOrder = []; | ||
| for (const [alias, infos] of freshAliasGroups) { | ||
| const variants = infos.map(i => this.modelIdToModelVariant.get(i.id)); | ||
| const existingModel = this.modelAliasToModel.get(alias); | ||
| if (existingModel) { | ||
| existingModel._refreshVariants(variants); | ||
| refreshedAliasOrder.push(existingModel); | ||
| } | ||
| else { | ||
| const m = new Model(variants[0]); | ||
| for (let i = 1; i < variants.length; i++) { | ||
| m.addVariant(variants[i]); | ||
| } | ||
| this.modelAliasToModel.set(alias, m); | ||
| refreshedAliasOrder.push(m); | ||
| } | ||
| } | ||
| this._models = refreshedAliasOrder; | ||
| this.lastFetch = Date.now(); | ||
| } | ||
| /** | ||
| * Lists all available models in the catalog. | ||
| * This method is asynchronous as it may fetch the model list from a remote service or perform file I/O. | ||
| * @returns A Promise that resolves to an array of IModel objects. | ||
| */ | ||
| async getModels() { | ||
| await this.updateModels(); | ||
| return this._models; | ||
| } | ||
| /** | ||
| * Retrieves a model by its alias. | ||
| * This method is asynchronous as it may ensure the catalog is up-to-date by fetching from a remote service. | ||
| * @param alias - The alias of the model to retrieve. | ||
| * @returns A Promise that resolves to the IModel object if found, otherwise throws an error. | ||
| * @throws Error - If alias is null, undefined, or empty. | ||
| */ | ||
| async getModel(alias) { | ||
| if (typeof alias !== 'string' || alias.trim() === '') { | ||
| throw new Error('Model alias must be a non-empty string.'); | ||
| } | ||
| await this.updateModels(); | ||
| let model = this.modelAliasToModel.get(alias); | ||
| if (!model) { | ||
| // Self-heal: the alias may belong to a BYOM model added to the | ||
| // cache directory after our last catalog refresh. | ||
| await this.updateModels(true); | ||
| model = this.modelAliasToModel.get(alias); | ||
| } | ||
| if (!model) { | ||
| const availableAliases = Array.from(this.modelAliasToModel.keys()).join(', '); | ||
| throw new Error(`Model with alias '${alias}' not found. Available models: ${availableAliases || '(none)'}`); | ||
| } | ||
| return model; | ||
| } | ||
| /** | ||
| * Retrieves a specific model variant by its ID. | ||
| * NOTE: This will return an IModel with a single variant. Use getModel to get an IModel with all available | ||
| * variants. | ||
| * This method is asynchronous as it may ensure the catalog is up-to-date by fetching from a remote service. | ||
| * @param modelId - The unique identifier of the model variant. | ||
| * @returns A Promise that resolves to the IModel object if found, otherwise throws an error. | ||
| * @throws Error - If modelId is null, undefined, or empty. | ||
| */ | ||
| async getModelVariant(modelId) { | ||
| if (typeof modelId !== 'string' || modelId.trim() === '') { | ||
| throw new Error('Model ID must be a non-empty string.'); | ||
| } | ||
| await this.updateModels(); | ||
| let variant = this.modelIdToModelVariant.get(modelId); | ||
| if (!variant) { | ||
| // Self-heal: the id may belong to a BYOM model added to the cache | ||
| // directory after our last catalog refresh. | ||
| await this.updateModels(true); | ||
| variant = this.modelIdToModelVariant.get(modelId); | ||
| } | ||
| if (!variant) { | ||
| const availableIds = Array.from(this.modelIdToModelVariant.keys()).join(', '); | ||
| throw new Error(`Model variant with ID '${modelId}' not found. Available variants: ${availableIds || '(none)'}`); | ||
| } | ||
| return variant; | ||
| } | ||
| /** | ||
| * Retrieves a list of all locally cached model variants. | ||
| * This method is asynchronous as it may involve file I/O or querying the underlying core. | ||
| * @returns A Promise that resolves to an array of cached IModel objects. | ||
| */ | ||
| async getCachedModels() { | ||
| await this.updateModels(); | ||
| const cachedModelListJson = await this.coreInterop.executeCommandAsync("get_cached_models"); | ||
| let cachedModelIds = []; | ||
| try { | ||
| cachedModelIds = JSON.parse(cachedModelListJson); | ||
| } | ||
| catch (error) { | ||
| throw new Error(`Failed to parse cached model list JSON: ${error}`); | ||
| } | ||
| return this.resolveModelIds(cachedModelIds); | ||
| } | ||
| /** | ||
| * Retrieves a list of all currently loaded model variants. | ||
| * This operation is asynchronous because checking the loaded status may involve querying | ||
| * the underlying core or an external service, which can be an I/O bound operation. | ||
| * @returns A Promise that resolves to an array of loaded IModel objects. | ||
| */ | ||
| async getLoadedModels() { | ||
| await this.updateModels(); | ||
| let loadedModelIds = []; | ||
| try { | ||
| loadedModelIds = await this.modelLoadManager.listLoaded(); | ||
| } | ||
| catch (error) { | ||
| throw new Error(`Failed to list loaded models: ${error}`); | ||
| } | ||
| return this.resolveModelIds(loadedModelIds); | ||
| } | ||
| /** | ||
| * Resolve a list of model ids against the in-memory catalog, self-healing once | ||
| * if any id is unknown (e.g. a manually-added BYOM model the SDK has not yet seen). | ||
| * Preserves the input order of `modelIds` (minus unknowns), deduplicating variants. | ||
| */ | ||
| async resolveModelIds(modelIds) { | ||
| if (modelIds.some(id => !this.modelIdToModelVariant.has(id))) { | ||
| await this.updateModels(true); | ||
| } | ||
| const resolved = []; | ||
| const seen = new Set(); | ||
| for (const modelId of modelIds) { | ||
| const variant = this.modelIdToModelVariant.get(modelId); | ||
| if (variant && !seen.has(variant)) { | ||
| resolved.push(variant); | ||
| seen.add(variant); | ||
| } | ||
| } | ||
| return resolved; | ||
| } | ||
| /** | ||
| * Get the latest version of a model. | ||
| * This is used to check if a newer version of a model is available in the catalog for download. | ||
| * @param modelOrModelVariant - The model to check for the latest version. | ||
| * @returns The latest version of the model. Will match the input if it is the latest version. | ||
| */ | ||
| async getLatestVersion(modelOrModelVariant) { | ||
| await this.updateModels(); | ||
| // Resolve to the parent Model by alias | ||
| const model = this.modelAliasToModel.get(modelOrModelVariant.alias); | ||
| if (!model) { | ||
| throw new Error(`Model with alias '${modelOrModelVariant.alias}' not found in catalog.`); | ||
| } | ||
| // variants are sorted by version, so the first one matching the name is the latest version | ||
| const latest = model.variants.find(v => v.info.name === modelOrModelVariant.info.name); | ||
| if (!latest) { | ||
| throw new Error(`Internal error. Mismatch between model (alias:${model.alias}) and ` + | ||
| `model variant (alias:${modelOrModelVariant.alias}).`); | ||
| } | ||
| // if input was the latest return the input (could be model or model variant) | ||
| // otherwise return the latest model variant | ||
| return latest.id === modelOrModelVariant.id ? modelOrModelVariant : latest; | ||
| } | ||
| } | ||
| //# sourceMappingURL=catalog.js.map |
| /** | ||
| * Configuration options for the Foundry Local SDK. | ||
| * Use a plain object with these properties to configure the SDK. | ||
| */ | ||
| export interface FoundryLocalConfig { | ||
| /** | ||
| * **REQUIRED** The name of the application using the SDK. | ||
| * Used for identifying the application in logs and telemetry. | ||
| */ | ||
| appName: string; | ||
| /** | ||
| * The directory where application data should be stored. | ||
| * Optional. Defaults to `{user_home}/.{appName}`. | ||
| */ | ||
| appDataDir?: string; | ||
| /** | ||
| * The directory where models are downloaded and cached. | ||
| * Optional. Defaults to `{appDataDir}/cache/models`. | ||
| */ | ||
| modelCacheDir?: string; | ||
| /** | ||
| * The directory where log files are written. | ||
| * Optional. Defaults to `{appDataDir}/logs`. | ||
| */ | ||
| logsDir?: string; | ||
| /** | ||
| * The logging level for the SDK. | ||
| * Optional. Valid values: 'trace', 'debug', 'info', 'warn', 'error', 'fatal'. | ||
| * Defaults to 'warn'. | ||
| */ | ||
| logLevel?: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal'; | ||
| /** | ||
| * The URL(s) for the local web service to bind to. | ||
| * Optional. Multiple URLs can be separated by semicolons. | ||
| * Example: "http://127.0.0.1:8080" | ||
| */ | ||
| webServiceUrls?: string; | ||
| /** | ||
| * The external URL if the web service is running in a separate process. | ||
| * Optional. This is used to connect to an existing service instance. | ||
| */ | ||
| serviceEndpoint?: string; | ||
| /** | ||
| * The path to the directory containing the native Foundry Local Core libraries. | ||
| * Optional. This directory must contain `Microsoft.AI.Foundry.Local.Core`, `onnxruntime`, and `onnxruntime-genai` binaries. | ||
| * If not provided, the SDK attempts to discover them in standard locations. | ||
| */ | ||
| libraryPath?: string; | ||
| /** | ||
| * Additional settings to pass to the core. | ||
| * Optional. Internal use only. | ||
| */ | ||
| additionalSettings?: { | ||
| [key: string]: string; | ||
| }; | ||
| } | ||
| export declare class Configuration { | ||
| params: { | ||
| [key: string]: string; | ||
| }; | ||
| constructor(config: FoundryLocalConfig); | ||
| } |
| // Log level mapping from JS-style to C#-style | ||
| const LOG_LEVEL_MAP = { | ||
| 'trace': 'Verbose', | ||
| 'debug': 'Debug', | ||
| 'info': 'Information', | ||
| 'warn': 'Warning', | ||
| 'error': 'Error', | ||
| 'fatal': 'Fatal' | ||
| }; | ||
| // Internal Configuration class (not exported) | ||
| export class Configuration { | ||
| params; | ||
| constructor(config) { | ||
| if (!config) { | ||
| throw new Error("Configuration must be provided."); | ||
| } | ||
| if (!config.appName || config.appName.trim() === "") { | ||
| throw new Error("appName must be set to a valid application name."); | ||
| } | ||
| this.params = { | ||
| 'AppName': config.appName | ||
| }; | ||
| if (config.appDataDir) | ||
| this.params['AppDataDir'] = config.appDataDir; | ||
| if (config.modelCacheDir) | ||
| this.params['ModelCacheDir'] = config.modelCacheDir; | ||
| if (config.logsDir) | ||
| this.params['LogsDir'] = config.logsDir; | ||
| if (config.logLevel) | ||
| this.params['LogLevel'] = LOG_LEVEL_MAP[config.logLevel] || config.logLevel; | ||
| if (config.webServiceUrls) | ||
| this.params['WebServiceUrls'] = config.webServiceUrls; | ||
| if (config.serviceEndpoint) | ||
| this.params['WebServiceExternalUrl'] = config.serviceEndpoint; | ||
| if (config.libraryPath) | ||
| this.params['FoundryLocalCorePath'] = config.libraryPath; | ||
| // Flatten additional settings into params | ||
| if (config.additionalSettings) { | ||
| for (const key in config.additionalSettings) { | ||
| this.params[key] = config.additionalSettings[key]; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| //# sourceMappingURL=configuration.js.map |
| import { Configuration } from '../configuration.js'; | ||
| export declare class CoreInterop { | ||
| private addon; | ||
| private static _getLibraryExtension; | ||
| private static _resolveDefaultCorePath; | ||
| constructor(config: Configuration); | ||
| executeCommand(command: string, params?: any): string; | ||
| /** | ||
| * Asynchronously execute a native command without blocking the event loop. | ||
| * Runs the native call on a libuv worker thread. | ||
| */ | ||
| executeCommandAsync(command: string, params?: any): Promise<string>; | ||
| /** | ||
| * Execute a native command with binary data (e.g., audio PCM bytes). | ||
| * Uses the execute_command_with_binary native entry point which accepts | ||
| * both JSON params and raw binary data via StreamingRequestBuffer. | ||
| */ | ||
| executeCommandWithBinary(command: string, params: any, binaryData: Uint8Array): string; | ||
| executeCommandStreaming(command: string, params: any, callback: (chunk: string) => void, signal?: AbortSignal): Promise<string>; | ||
| } |
| import path from 'path'; | ||
| import fs from 'fs'; | ||
| import { createRequire } from 'module'; | ||
| import { fileURLToPath } from 'url'; | ||
| const __filename = fileURLToPath(import.meta.url); | ||
| const __dirname = path.dirname(__filename); | ||
| // Load the prebuilt Node-API addon | ||
| const require = createRequire(import.meta.url); | ||
| function loadAddon() { | ||
| const platform = process.platform; | ||
| const arch = process.arch; | ||
| const platformKey = `${platform}-${arch}`; | ||
| // The prebuilt addon ships inside the SDK package under prebuilds/<platform>/ | ||
| const sdkRoot = path.resolve(__dirname, '..', '..'); | ||
| const prebuiltPath = path.join(sdkRoot, 'prebuilds', platformKey, 'foundry_local_napi.node'); | ||
| if (fs.existsSync(prebuiltPath)) { | ||
| return require(prebuiltPath); | ||
| } | ||
| // Fallback: development builds from node-gyp (sdk contributors) | ||
| const devPath = path.join(sdkRoot, 'native', 'build', 'Release', 'foundry_local_napi.node'); | ||
| if (fs.existsSync(devPath)) { | ||
| return require(devPath); | ||
| } | ||
| throw new Error(`Could not find foundry_local_napi.node for platform ${platformKey}. ` + | ||
| `Searched: ${prebuiltPath}, ${devPath}. ` + | ||
| `Please ensure the SDK was installed correctly or run 'npm run build:native' to compile from source.`); | ||
| } | ||
| export class CoreInterop { | ||
| addon; | ||
| static _getLibraryExtension() { | ||
| const platform = process.platform; | ||
| if (platform === 'win32') | ||
| return '.dll'; | ||
| if (platform === 'linux') | ||
| return '.so'; | ||
| if (platform === 'darwin') | ||
| return '.dylib'; | ||
| throw new Error(`Unsupported platform: ${platform}`); | ||
| } | ||
| static _resolveDefaultCorePath(config) { | ||
| const platform = process.platform; | ||
| const arch = process.arch; | ||
| const platformKey = `${platform}-${arch}`; | ||
| // Resolve the native binary directory at foundry-local-core/<platform>, | ||
| // the shared location where install scripts place the native binaries. | ||
| const sdkRoot = path.resolve(__dirname, '..', '..'); | ||
| const packageDir = path.join(sdkRoot, 'foundry-local-core', platformKey); | ||
| const ext = CoreInterop._getLibraryExtension(); | ||
| const corePath = path.join(packageDir, `Microsoft.AI.Foundry.Local.Core${ext}`); | ||
| if (fs.existsSync(corePath)) { | ||
| config.params['FoundryLocalCorePath'] = corePath; | ||
| return corePath; | ||
| } | ||
| return null; | ||
| } | ||
| constructor(config) { | ||
| this.addon = loadAddon(); | ||
| const corePath = config.params['FoundryLocalCorePath'] || CoreInterop._resolveDefaultCorePath(config); | ||
| if (!corePath) { | ||
| throw new Error("FoundryLocalCorePath not specified in configuration and could not auto-discover binaries. Please run 'npm install' to download native libraries."); | ||
| } | ||
| const coreDir = path.dirname(corePath); | ||
| const ext = CoreInterop._getLibraryExtension(); | ||
| // On Windows, explicitly load dependencies to work around DLL resolution challenges | ||
| const depPaths = []; | ||
| if (process.platform === 'win32') { | ||
| depPaths.push(path.join(coreDir, `onnxruntime${ext}`)); | ||
| depPaths.push(path.join(coreDir, `onnxruntime-genai${ext}`)); | ||
| const currentPath = process.env.PATH ?? ''; | ||
| process.env.PATH = currentPath ? `${coreDir};${currentPath}` : coreDir; | ||
| } | ||
| this.addon.loadLibrary(corePath, depPaths.length > 0 ? depPaths : undefined); | ||
| } | ||
| executeCommand(command, params) { | ||
| const dataStr = params ? JSON.stringify(params) : ''; | ||
| return this.addon.executeCommand(command, dataStr); | ||
| } | ||
| /** | ||
| * Asynchronously execute a native command without blocking the event loop. | ||
| * Runs the native call on a libuv worker thread. | ||
| */ | ||
| executeCommandAsync(command, params) { | ||
| const dataStr = params ? JSON.stringify(params) : ''; | ||
| return this.addon.executeCommandAsync(command, dataStr); | ||
| } | ||
| /** | ||
| * Execute a native command with binary data (e.g., audio PCM bytes). | ||
| * Uses the execute_command_with_binary native entry point which accepts | ||
| * both JSON params and raw binary data via StreamingRequestBuffer. | ||
| */ | ||
| executeCommandWithBinary(command, params, binaryData) { | ||
| const dataStr = params ? JSON.stringify(params) : ''; | ||
| const binBuf = Buffer.from(binaryData.buffer, binaryData.byteOffset, binaryData.byteLength); | ||
| return this.addon.executeCommandWithBinary(command, dataStr, binBuf); | ||
| } | ||
| async executeCommandStreaming(command, params, callback, signal) { | ||
| const createAbortError = () => { | ||
| const error = new Error('Operation cancelled'); | ||
| error.name = 'AbortError'; | ||
| return error; | ||
| }; | ||
| if (signal?.aborted) { | ||
| throw createAbortError(); | ||
| } | ||
| const dataStr = params ? JSON.stringify(params) : ''; | ||
| let cancelled = false; | ||
| const wrappedCallback = (chunk) => { | ||
| if (signal?.aborted) { | ||
| cancelled = true; | ||
| throw createAbortError(); | ||
| } | ||
| callback(chunk); | ||
| }; | ||
| try { | ||
| const result = await this.addon.executeCommandStreaming(command, dataStr, wrappedCallback); | ||
| if (cancelled) { | ||
| throw createAbortError(); | ||
| } | ||
| return result; | ||
| } | ||
| catch (error) { | ||
| if (cancelled) { | ||
| throw createAbortError(); | ||
| } | ||
| throw error; | ||
| } | ||
| } | ||
| } | ||
| //# sourceMappingURL=coreInterop.js.map |
| import { ModelVariant } from './modelVariant.js'; | ||
| import { ChatClient } from '../openai/chatClient.js'; | ||
| import { AudioClient } from '../openai/audioClient.js'; | ||
| import { EmbeddingClient } from '../openai/embeddingClient.js'; | ||
| import { ResponsesClient } from '../openai/responsesClient.js'; | ||
| import { LiveAudioTranscriptionSession } from '../openai/liveAudioSession.js'; | ||
| import { IModel } from '../imodel.js'; | ||
| import { ModelInfo } from '../types.js'; | ||
| /** | ||
| * Represents a high-level AI model that may have multiple variants (e.g., quantized versions, different formats). | ||
| * Manages the selection and interaction with a specific model variant. | ||
| */ | ||
| export declare class Model implements IModel { | ||
| private _alias; | ||
| private _variants; | ||
| private selectedVariant; | ||
| constructor(variant: ModelVariant); | ||
| /** | ||
| * Adds a new variant to this model. | ||
| * Automatically selects the new variant if it is cached and the current one is not. | ||
| * @param variant - The model variant to add. | ||
| * @throws Error - If the variant's alias does not match the model's alias. | ||
| * @internal | ||
| */ | ||
| addVariant(variant: ModelVariant): void; | ||
| /** | ||
| * Replace the variant list in place while preserving wrapper identity. | ||
| * | ||
| * Called by `Catalog.fetchAndPopulateModels` during incremental refresh | ||
| * so a user's held `Model` reference keeps pointing at the same object | ||
| * across refreshes. Because `Catalog.fetchAndPopulateModels` reuses the | ||
| * same `ModelVariant` wrappers for ids that survive a refresh, any | ||
| * explicit `selectVariant()` choice that survives the refresh is | ||
| * preserved without any extra work here. | ||
| * | ||
| * TODO: tighten the held-reference contract for the case where the | ||
| * previously selected variant is removed by a refresh; today | ||
| * `selectedVariant` keeps pointing at the dropped wrapper and callers | ||
| * must explicitly re-select. | ||
| * | ||
| * @internal | ||
| */ | ||
| _refreshVariants(variants: ModelVariant[]): void; | ||
| /** | ||
| * Selects a specific variant. | ||
| * @param variant - The model variant to select. Must be one of the variants in `variants`. | ||
| * @throws Error - If the variant does not belong to this model. | ||
| */ | ||
| selectVariant(variant: IModel): void; | ||
| /** | ||
| * Gets the ID of the currently selected variant. | ||
| * @returns The ID of the selected variant. | ||
| */ | ||
| get id(): string; | ||
| /** | ||
| * Gets the alias of the model. | ||
| * @returns The model alias. | ||
| */ | ||
| get alias(): string; | ||
| /** | ||
| * Gets the ModelInfo of the currently selected variant. | ||
| * @returns The ModelInfo object. | ||
| */ | ||
| get info(): ModelInfo; | ||
| /** | ||
| * Checks if the currently selected variant is cached locally. | ||
| * @returns True if cached, false otherwise. | ||
| */ | ||
| get isCached(): boolean; | ||
| /** | ||
| * Checks if the currently selected variant is loaded in memory. | ||
| * @returns True if loaded, false otherwise. | ||
| */ | ||
| isLoaded(): Promise<boolean>; | ||
| /** | ||
| * Gets all available variants for this model. | ||
| * @returns An array of IModel objects. | ||
| */ | ||
| get variants(): IModel[]; | ||
| get contextLength(): number | null; | ||
| get inputModalities(): string | null; | ||
| get outputModalities(): string | null; | ||
| get capabilities(): string | null; | ||
| get supportsToolCalling(): boolean | null; | ||
| /** | ||
| * Downloads the currently selected variant. | ||
| * @param progressCallbackOrSignal - Optional progress callback or AbortSignal. | ||
| * @param signal - Optional AbortSignal when a progress callback is provided. | ||
| */ | ||
| download(progressCallbackOrSignal?: ((progress: number) => void) | AbortSignal, signal?: AbortSignal): Promise<void>; | ||
| /** | ||
| * Gets the local file path of the currently selected variant. | ||
| * @returns The local file path. | ||
| */ | ||
| get path(): string; | ||
| /** | ||
| * Loads the currently selected variant into memory. | ||
| * @returns A promise that resolves when the model is loaded. | ||
| */ | ||
| load(): Promise<void>; | ||
| /** | ||
| * Removes the currently selected variant from the local cache. | ||
| */ | ||
| removeFromCache(): void; | ||
| /** | ||
| * Unloads the currently selected variant from memory. | ||
| * @returns A promise that resolves when the model is unloaded. | ||
| */ | ||
| unload(): Promise<void>; | ||
| /** | ||
| * Creates a ChatClient for interacting with the model via chat completions. | ||
| * @returns A ChatClient instance. | ||
| */ | ||
| createChatClient(): ChatClient; | ||
| /** | ||
| * Creates an AudioClient for interacting with the model via audio operations. | ||
| * @returns An AudioClient instance. | ||
| */ | ||
| createAudioClient(): AudioClient; | ||
| /** | ||
| * Creates an EmbeddingClient for generating text embeddings with the model. | ||
| * @returns An EmbeddingClient instance. | ||
| */ | ||
| createEmbeddingClient(): EmbeddingClient; | ||
| /** | ||
| * Creates a LiveAudioTranscriptionSession for real-time audio streaming ASR. | ||
| * @returns A LiveAudioTranscriptionSession instance. | ||
| */ | ||
| createLiveTranscriptionSession(): LiveAudioTranscriptionSession; | ||
| /** | ||
| * Creates a ResponsesClient for interacting with the model via the Responses API. | ||
| * @param baseUrl - The base URL of the Foundry Local web service. | ||
| * @returns A ResponsesClient instance. | ||
| */ | ||
| createResponsesClient(baseUrl: string): ResponsesClient; | ||
| } |
| /** | ||
| * Represents a high-level AI model that may have multiple variants (e.g., quantized versions, different formats). | ||
| * Manages the selection and interaction with a specific model variant. | ||
| */ | ||
| export class Model { | ||
| _alias; | ||
| _variants; | ||
| selectedVariant; | ||
| constructor(variant) { | ||
| this._alias = variant.alias; | ||
| this._variants = [variant]; | ||
| this.selectedVariant = variant; | ||
| } | ||
| /** | ||
| * Adds a new variant to this model. | ||
| * Automatically selects the new variant if it is cached and the current one is not. | ||
| * @param variant - The model variant to add. | ||
| * @throws Error - If the variant's alias does not match the model's alias. | ||
| * @internal | ||
| */ | ||
| addVariant(variant) { | ||
| if (!variant || variant.alias !== this._alias) { | ||
| throw new Error(`Variant alias "${variant?.alias}" does not match model alias "${this._alias}".`); | ||
| } | ||
| this._variants.push(variant); | ||
| // Prefer the highest priority locally cached variant. | ||
| if (variant.info.cached && !this.selectedVariant.info.cached) { | ||
| this.selectedVariant = variant; | ||
| } | ||
| } | ||
| /** | ||
| * Replace the variant list in place while preserving wrapper identity. | ||
| * | ||
| * Called by `Catalog.fetchAndPopulateModels` during incremental refresh | ||
| * so a user's held `Model` reference keeps pointing at the same object | ||
| * across refreshes. Because `Catalog.fetchAndPopulateModels` reuses the | ||
| * same `ModelVariant` wrappers for ids that survive a refresh, any | ||
| * explicit `selectVariant()` choice that survives the refresh is | ||
| * preserved without any extra work here. | ||
| * | ||
| * TODO: tighten the held-reference contract for the case where the | ||
| * previously selected variant is removed by a refresh; today | ||
| * `selectedVariant` keeps pointing at the dropped wrapper and callers | ||
| * must explicitly re-select. | ||
| * | ||
| * @internal | ||
| */ | ||
| _refreshVariants(variants) { | ||
| if (!variants || variants.length === 0) { | ||
| throw new Error(`Cannot refresh model ${this._alias} with an empty variant list`); | ||
| } | ||
| this._variants = variants.slice(); | ||
| } | ||
| /** | ||
| * Selects a specific variant. | ||
| * @param variant - The model variant to select. Must be one of the variants in `variants`. | ||
| * @throws Error - If the variant does not belong to this model. | ||
| */ | ||
| selectVariant(variant) { | ||
| const matchingVariant = this._variants.find(v => v.id === variant.id); | ||
| if (!variant.id || !matchingVariant) { | ||
| throw new Error(`Input variant was not found in Variants.`); | ||
| } | ||
| this.selectedVariant = matchingVariant; | ||
| } | ||
| /** | ||
| * Gets the ID of the currently selected variant. | ||
| * @returns The ID of the selected variant. | ||
| */ | ||
| get id() { | ||
| return this.selectedVariant.id; | ||
| } | ||
| /** | ||
| * Gets the alias of the model. | ||
| * @returns The model alias. | ||
| */ | ||
| get alias() { | ||
| return this._alias; | ||
| } | ||
| /** | ||
| * Gets the ModelInfo of the currently selected variant. | ||
| * @returns The ModelInfo object. | ||
| */ | ||
| get info() { | ||
| return this.selectedVariant.info; | ||
| } | ||
| /** | ||
| * Checks if the currently selected variant is cached locally. | ||
| * @returns True if cached, false otherwise. | ||
| */ | ||
| get isCached() { | ||
| return this.selectedVariant.isCached; | ||
| } | ||
| /** | ||
| * Checks if the currently selected variant is loaded in memory. | ||
| * @returns True if loaded, false otherwise. | ||
| */ | ||
| async isLoaded() { | ||
| return await this.selectedVariant.isLoaded(); | ||
| } | ||
| /** | ||
| * Gets all available variants for this model. | ||
| * @returns An array of IModel objects. | ||
| */ | ||
| get variants() { | ||
| return this._variants; | ||
| } | ||
| get contextLength() { | ||
| return this.selectedVariant.contextLength; | ||
| } | ||
| get inputModalities() { | ||
| return this.selectedVariant.inputModalities; | ||
| } | ||
| get outputModalities() { | ||
| return this.selectedVariant.outputModalities; | ||
| } | ||
| get capabilities() { | ||
| return this.selectedVariant.capabilities; | ||
| } | ||
| get supportsToolCalling() { | ||
| return this.selectedVariant.supportsToolCalling; | ||
| } | ||
| /** | ||
| * Downloads the currently selected variant. | ||
| * @param progressCallbackOrSignal - Optional progress callback or AbortSignal. | ||
| * @param signal - Optional AbortSignal when a progress callback is provided. | ||
| */ | ||
| download(progressCallbackOrSignal, signal) { | ||
| return this.selectedVariant.download(progressCallbackOrSignal, signal); | ||
| } | ||
| /** | ||
| * Gets the local file path of the currently selected variant. | ||
| * @returns The local file path. | ||
| */ | ||
| get path() { | ||
| return this.selectedVariant.path; | ||
| } | ||
| /** | ||
| * Loads the currently selected variant into memory. | ||
| * @returns A promise that resolves when the model is loaded. | ||
| */ | ||
| async load() { | ||
| await this.selectedVariant.load(); | ||
| } | ||
| /** | ||
| * Removes the currently selected variant from the local cache. | ||
| */ | ||
| removeFromCache() { | ||
| this.selectedVariant.removeFromCache(); | ||
| } | ||
| /** | ||
| * Unloads the currently selected variant from memory. | ||
| * @returns A promise that resolves when the model is unloaded. | ||
| */ | ||
| async unload() { | ||
| await this.selectedVariant.unload(); | ||
| } | ||
| /** | ||
| * Creates a ChatClient for interacting with the model via chat completions. | ||
| * @returns A ChatClient instance. | ||
| */ | ||
| createChatClient() { | ||
| return this.selectedVariant.createChatClient(); | ||
| } | ||
| /** | ||
| * Creates an AudioClient for interacting with the model via audio operations. | ||
| * @returns An AudioClient instance. | ||
| */ | ||
| createAudioClient() { | ||
| return this.selectedVariant.createAudioClient(); | ||
| } | ||
| /** | ||
| * Creates an EmbeddingClient for generating text embeddings with the model. | ||
| * @returns An EmbeddingClient instance. | ||
| */ | ||
| createEmbeddingClient() { | ||
| return this.selectedVariant.createEmbeddingClient(); | ||
| } | ||
| /** | ||
| * Creates a LiveAudioTranscriptionSession for real-time audio streaming ASR. | ||
| * @returns A LiveAudioTranscriptionSession instance. | ||
| */ | ||
| createLiveTranscriptionSession() { | ||
| return this.selectedVariant.createLiveTranscriptionSession(); | ||
| } | ||
| /** | ||
| * Creates a ResponsesClient for interacting with the model via the Responses API. | ||
| * @param baseUrl - The base URL of the Foundry Local web service. | ||
| * @returns A ResponsesClient instance. | ||
| */ | ||
| createResponsesClient(baseUrl) { | ||
| return this.selectedVariant.createResponsesClient(baseUrl); | ||
| } | ||
| } | ||
| //# sourceMappingURL=model.js.map |
| import { CoreInterop } from './coreInterop.js'; | ||
| /** | ||
| * Manages the loading and unloading of models. | ||
| * Handles communication with the core system or an external service (future support). | ||
| */ | ||
| export declare class ModelLoadManager { | ||
| private coreInterop; | ||
| private externalServiceUrl?; | ||
| private headers; | ||
| constructor(coreInterop: CoreInterop, externalServiceUrl?: string); | ||
| /** | ||
| * Loads a model into memory. | ||
| * @param modelId - The ID of the model to load. | ||
| * @throws Error - If loading via external service fails. | ||
| */ | ||
| load(modelId: string): Promise<void>; | ||
| /** | ||
| * Unloads a model from memory. | ||
| * @param modelId - The ID of the model to unload. | ||
| * @throws Error - If unloading via external service fails. | ||
| */ | ||
| unload(modelId: string): Promise<void>; | ||
| /** | ||
| * Lists the IDs of all currently loaded models. | ||
| * @returns An array of loaded model IDs. | ||
| * @throws Error - If listing via external service fails or if JSON parsing fails. | ||
| */ | ||
| listLoaded(): Promise<string[]>; | ||
| } |
| import packageJson from '../../package.json' with { type: "json" }; | ||
| const { version } = packageJson; | ||
| /** | ||
| * Manages the loading and unloading of models. | ||
| * Handles communication with the core system or an external service (future support). | ||
| */ | ||
| export class ModelLoadManager { | ||
| coreInterop; | ||
| externalServiceUrl; | ||
| headers; | ||
| constructor(coreInterop, externalServiceUrl) { | ||
| this.coreInterop = coreInterop; | ||
| this.externalServiceUrl = externalServiceUrl; | ||
| this.headers = { | ||
| 'User-Agent': `foundry-local-js-sdk/${version}` | ||
| }; | ||
| } | ||
| /** | ||
| * Loads a model into memory. | ||
| * @param modelId - The ID of the model to load. | ||
| * @throws Error - If loading via external service fails. | ||
| */ | ||
| async load(modelId) { | ||
| if (this.externalServiceUrl) { | ||
| const url = new URL(`models/load/${encodeURIComponent(modelId)}`, this.externalServiceUrl); | ||
| try { | ||
| const response = await fetch(url.toString(), { headers: this.headers }); | ||
| if (!response.ok) { | ||
| throw new Error(`Error loading model ${modelId} from ${this.externalServiceUrl}: ${response.statusText}`); | ||
| } | ||
| } | ||
| catch (error) { | ||
| throw new Error(`Network error occurred while loading model ${modelId} from ${this.externalServiceUrl}: ${error.message}`); | ||
| } | ||
| return; | ||
| } | ||
| await this.coreInterop.executeCommandAsync("load_model", { Params: { Model: modelId } }); | ||
| } | ||
| /** | ||
| * Unloads a model from memory. | ||
| * @param modelId - The ID of the model to unload. | ||
| * @throws Error - If unloading via external service fails. | ||
| */ | ||
| async unload(modelId) { | ||
| if (this.externalServiceUrl) { | ||
| const url = new URL(`models/unload/${encodeURIComponent(modelId)}`, this.externalServiceUrl); | ||
| const response = await fetch(url.toString(), { headers: this.headers }); | ||
| if (!response.ok) { | ||
| throw new Error(`Error unloading model ${modelId} from ${this.externalServiceUrl}: ${response.statusText}`); | ||
| } | ||
| return; | ||
| } | ||
| await this.coreInterop.executeCommandAsync("unload_model", { Params: { Model: modelId } }); | ||
| } | ||
| /** | ||
| * Lists the IDs of all currently loaded models. | ||
| * @returns An array of loaded model IDs. | ||
| * @throws Error - If listing via external service fails or if JSON parsing fails. | ||
| */ | ||
| async listLoaded() { | ||
| if (this.externalServiceUrl) { | ||
| const url = new URL('models/loaded', this.externalServiceUrl); | ||
| const response = await fetch(url.toString(), { headers: this.headers }); | ||
| if (!response.ok) { | ||
| throw new Error(`Error listing loaded models from ${this.externalServiceUrl}: ${response.statusText}`); | ||
| } | ||
| const list = await response.json(); | ||
| return list || []; | ||
| } | ||
| const response = await this.coreInterop.executeCommandAsync("list_loaded_models"); | ||
| try { | ||
| return JSON.parse(response); | ||
| } | ||
| catch (error) { | ||
| throw new Error(`Failed to decode JSON response: ${error}. Response was: ${response}`); | ||
| } | ||
| } | ||
| } | ||
| //# sourceMappingURL=modelLoadManager.js.map |
| import { CoreInterop } from './coreInterop.js'; | ||
| import { ModelLoadManager } from './modelLoadManager.js'; | ||
| import { ModelInfo } from '../types.js'; | ||
| import { ChatClient } from '../openai/chatClient.js'; | ||
| import { AudioClient } from '../openai/audioClient.js'; | ||
| import { EmbeddingClient } from '../openai/embeddingClient.js'; | ||
| import { LiveAudioTranscriptionSession } from '../openai/liveAudioSession.js'; | ||
| import { ResponsesClient } from '../openai/responsesClient.js'; | ||
| import { IModel } from '../imodel.js'; | ||
| /** | ||
| * Represents a specific variant of a model (e.g., a specific quantization or format). | ||
| * Contains the low-level implementation for interacting with the model. | ||
| * @internal | ||
| */ | ||
| export declare class ModelVariant implements IModel { | ||
| private _modelInfo; | ||
| private coreInterop; | ||
| private modelLoadManager; | ||
| constructor(modelInfo: ModelInfo, coreInterop: CoreInterop, modelLoadManager: ModelLoadManager); | ||
| /** | ||
| * Replace the cached ModelInfo snapshot in place. | ||
| * | ||
| * Called by `Catalog.fetchAndPopulateModels` during incremental refresh so | ||
| * wrapper identity is preserved across refreshes while still surfacing | ||
| * fresh metadata (notably `cached`) on held references. `id` and `alias` | ||
| * are immutable for a given variant; the caller must only invoke this with | ||
| * a `modelInfo` whose id matches `this.id`. JS is single-threaded so no | ||
| * torn-read concerns. | ||
| * | ||
| * @internal | ||
| */ | ||
| _refreshInfo(modelInfo: ModelInfo): void; | ||
| /** | ||
| * Gets the unique identifier of the model variant. | ||
| * @returns The model ID. | ||
| */ | ||
| get id(): string; | ||
| /** | ||
| * Gets the alias of the model. | ||
| * @returns The model alias. | ||
| */ | ||
| get alias(): string; | ||
| /** | ||
| * Gets the detailed information about the model variant. | ||
| * @returns The ModelInfo object. | ||
| */ | ||
| get info(): ModelInfo; | ||
| /** | ||
| * A ModelVariant is a single variant, so variants returns itself. | ||
| */ | ||
| get variants(): IModel[]; | ||
| /** | ||
| * SelectVariant is not supported on a ModelVariant. | ||
| * Call Catalog.getModel() to get an IModel with all variants available. | ||
| * @throws Error always. | ||
| */ | ||
| selectVariant(_variant: IModel): void; | ||
| get contextLength(): number | null; | ||
| get inputModalities(): string | null; | ||
| get outputModalities(): string | null; | ||
| get capabilities(): string | null; | ||
| get supportsToolCalling(): boolean | null; | ||
| /** | ||
| * Checks if the model variant is cached locally. | ||
| * @returns True if cached, false otherwise. | ||
| */ | ||
| get isCached(): boolean; | ||
| /** | ||
| * Checks if the model variant is loaded in memory. | ||
| * @returns True if loaded, false otherwise. | ||
| */ | ||
| isLoaded(): Promise<boolean>; | ||
| /** | ||
| * Downloads the model variant. | ||
| * @param progressCallbackOrSignal - Optional progress callback (0-100) or AbortSignal. | ||
| * @param signal - Optional AbortSignal when a progress callback is provided. | ||
| */ | ||
| download(progressCallbackOrSignal?: ((progress: number) => void) | AbortSignal, signal?: AbortSignal): Promise<void>; | ||
| /** | ||
| * Gets the local file path of the model variant. | ||
| * @returns The local file path. | ||
| */ | ||
| get path(): string; | ||
| /** | ||
| * Loads the model variant into memory. | ||
| * @returns A promise that resolves when the model is loaded. | ||
| */ | ||
| load(): Promise<void>; | ||
| /** | ||
| * Removes the model variant from the local cache. | ||
| */ | ||
| removeFromCache(): void; | ||
| /** | ||
| * Unloads the model variant from memory. | ||
| * @returns A promise that resolves when the model is unloaded. | ||
| */ | ||
| unload(): Promise<void>; | ||
| /** | ||
| * Creates a ChatClient for interacting with the model via chat completions. | ||
| * @returns A ChatClient instance. | ||
| */ | ||
| createChatClient(): ChatClient; | ||
| /** | ||
| * Creates an AudioClient for interacting with the model via audio operations. | ||
| * @returns An AudioClient instance. | ||
| */ | ||
| createAudioClient(): AudioClient; | ||
| /** | ||
| * Creates an EmbeddingClient for generating text embeddings with the model. | ||
| * @returns An EmbeddingClient instance. | ||
| */ | ||
| createEmbeddingClient(): EmbeddingClient; | ||
| /** | ||
| * Creates a LiveAudioTranscriptionSession for real-time audio streaming ASR. | ||
| * @returns A LiveAudioTranscriptionSession instance. | ||
| */ | ||
| createLiveTranscriptionSession(): LiveAudioTranscriptionSession; | ||
| /** | ||
| * Creates a ResponsesClient for interacting with the model via the Responses API. | ||
| * @param baseUrl - The base URL of the Foundry Local web service. | ||
| * @returns A ResponsesClient instance. | ||
| */ | ||
| createResponsesClient(baseUrl: string): ResponsesClient; | ||
| } |
| import { ChatClient } from '../openai/chatClient.js'; | ||
| import { AudioClient } from '../openai/audioClient.js'; | ||
| import { EmbeddingClient } from '../openai/embeddingClient.js'; | ||
| import { LiveAudioTranscriptionSession } from '../openai/liveAudioSession.js'; | ||
| import { ResponsesClient } from '../openai/responsesClient.js'; | ||
| /** | ||
| * Represents a specific variant of a model (e.g., a specific quantization or format). | ||
| * Contains the low-level implementation for interacting with the model. | ||
| * @internal | ||
| */ | ||
| export class ModelVariant { | ||
| _modelInfo; | ||
| coreInterop; | ||
| modelLoadManager; | ||
| constructor(modelInfo, coreInterop, modelLoadManager) { | ||
| this._modelInfo = modelInfo; | ||
| this.coreInterop = coreInterop; | ||
| this.modelLoadManager = modelLoadManager; | ||
| } | ||
| /** | ||
| * Replace the cached ModelInfo snapshot in place. | ||
| * | ||
| * Called by `Catalog.fetchAndPopulateModels` during incremental refresh so | ||
| * wrapper identity is preserved across refreshes while still surfacing | ||
| * fresh metadata (notably `cached`) on held references. `id` and `alias` | ||
| * are immutable for a given variant; the caller must only invoke this with | ||
| * a `modelInfo` whose id matches `this.id`. JS is single-threaded so no | ||
| * torn-read concerns. | ||
| * | ||
| * @internal | ||
| */ | ||
| _refreshInfo(modelInfo) { | ||
| this._modelInfo = modelInfo; | ||
| } | ||
| /** | ||
| * Gets the unique identifier of the model variant. | ||
| * @returns The model ID. | ||
| */ | ||
| get id() { | ||
| return this._modelInfo.id; | ||
| } | ||
| /** | ||
| * Gets the alias of the model. | ||
| * @returns The model alias. | ||
| */ | ||
| get alias() { | ||
| return this._modelInfo.alias; | ||
| } | ||
| /** | ||
| * Gets the detailed information about the model variant. | ||
| * @returns The ModelInfo object. | ||
| */ | ||
| get info() { | ||
| return this._modelInfo; | ||
| } | ||
| /** | ||
| * A ModelVariant is a single variant, so variants returns itself. | ||
| */ | ||
| get variants() { | ||
| return [this]; | ||
| } | ||
| /** | ||
| * SelectVariant is not supported on a ModelVariant. | ||
| * Call Catalog.getModel() to get an IModel with all variants available. | ||
| * @throws Error always. | ||
| */ | ||
| selectVariant(_variant) { | ||
| throw new Error(`selectVariant is not supported on a ModelVariant. ` + | ||
| `Call Catalog.getModel("${this.alias}") to get an IModel with all variants available.`); | ||
| } | ||
| get contextLength() { | ||
| return this._modelInfo.contextLength ?? null; | ||
| } | ||
| get inputModalities() { | ||
| return this._modelInfo.inputModalities ?? null; | ||
| } | ||
| get outputModalities() { | ||
| return this._modelInfo.outputModalities ?? null; | ||
| } | ||
| get capabilities() { | ||
| return this._modelInfo.capabilities ?? null; | ||
| } | ||
| get supportsToolCalling() { | ||
| return this._modelInfo.supportsToolCalling ?? null; | ||
| } | ||
| /** | ||
| * Checks if the model variant is cached locally. | ||
| * @returns True if cached, false otherwise. | ||
| */ | ||
| get isCached() { | ||
| const cachedModels = JSON.parse(this.coreInterop.executeCommand("get_cached_models")); | ||
| return cachedModels.includes(this._modelInfo.id); | ||
| } | ||
| /** | ||
| * Checks if the model variant is loaded in memory. | ||
| * @returns True if loaded, false otherwise. | ||
| */ | ||
| async isLoaded() { | ||
| const loadedModels = await this.modelLoadManager.listLoaded(); | ||
| return loadedModels.includes(this._modelInfo.id); | ||
| } | ||
| /** | ||
| * Downloads the model variant. | ||
| * @param progressCallbackOrSignal - Optional progress callback (0-100) or AbortSignal. | ||
| * @param signal - Optional AbortSignal when a progress callback is provided. | ||
| */ | ||
| async download(progressCallbackOrSignal, signal) { | ||
| const progressCallback = typeof progressCallbackOrSignal === 'function' | ||
| ? progressCallbackOrSignal | ||
| : undefined; | ||
| const abortSignal = typeof progressCallbackOrSignal === 'function' | ||
| ? signal | ||
| : progressCallbackOrSignal ?? signal; | ||
| const request = { Params: { Model: this._modelInfo.id } }; | ||
| if (!progressCallback && !abortSignal) { | ||
| await this.coreInterop.executeCommandAsync("download_model", request); | ||
| } | ||
| else { | ||
| // Use the streaming path when progress or cancellation is needed. | ||
| // Provide a no-op callback when only cancellation is requested so | ||
| // the native callback mechanism is engaged. | ||
| const cb = progressCallback ?? (() => { }); | ||
| await this.coreInterop.executeCommandStreaming("download_model", request, (chunk) => { | ||
| const progressChunk = chunk.trim(); | ||
| if (progressChunk.length === 0) { | ||
| return; | ||
| } | ||
| const progress = Number(progressChunk); | ||
| if (!Number.isNaN(progress)) { | ||
| cb(progress); | ||
| } | ||
| }, abortSignal); | ||
| } | ||
| } | ||
| /** | ||
| * Gets the local file path of the model variant. | ||
| * @returns The local file path. | ||
| */ | ||
| get path() { | ||
| const request = { Params: { Model: this._modelInfo.id } }; | ||
| return this.coreInterop.executeCommand("get_model_path", request); | ||
| } | ||
| /** | ||
| * Loads the model variant into memory. | ||
| * @returns A promise that resolves when the model is loaded. | ||
| */ | ||
| async load() { | ||
| await this.modelLoadManager.load(this._modelInfo.id); | ||
| } | ||
| /** | ||
| * Removes the model variant from the local cache. | ||
| */ | ||
| removeFromCache() { | ||
| this.coreInterop.executeCommand("remove_cached_model", { Params: { Model: this._modelInfo.id } }); | ||
| } | ||
| /** | ||
| * Unloads the model variant from memory. | ||
| * @returns A promise that resolves when the model is unloaded. | ||
| */ | ||
| async unload() { | ||
| await this.modelLoadManager.unload(this._modelInfo.id); | ||
| } | ||
| /** | ||
| * Creates a ChatClient for interacting with the model via chat completions. | ||
| * @returns A ChatClient instance. | ||
| */ | ||
| createChatClient() { | ||
| return new ChatClient(this._modelInfo.id, this.coreInterop); | ||
| } | ||
| /** | ||
| * Creates an AudioClient for interacting with the model via audio operations. | ||
| * @returns An AudioClient instance. | ||
| */ | ||
| createAudioClient() { | ||
| return new AudioClient(this._modelInfo.id, this.coreInterop); | ||
| } | ||
| /** | ||
| * Creates an EmbeddingClient for generating text embeddings with the model. | ||
| * @returns An EmbeddingClient instance. | ||
| */ | ||
| createEmbeddingClient() { | ||
| return new EmbeddingClient(this._modelInfo.id, this.coreInterop); | ||
| } | ||
| /** | ||
| * Creates a LiveAudioTranscriptionSession for real-time audio streaming ASR. | ||
| * @returns A LiveAudioTranscriptionSession instance. | ||
| */ | ||
| createLiveTranscriptionSession() { | ||
| return new LiveAudioTranscriptionSession(this._modelInfo.id, this.coreInterop); | ||
| } | ||
| /** | ||
| * Creates a ResponsesClient for interacting with the model via the Responses API. | ||
| * @param baseUrl - The base URL of the Foundry Local web service. | ||
| * @returns A ResponsesClient instance. | ||
| */ | ||
| createResponsesClient(baseUrl) { | ||
| return new ResponsesClient(baseUrl, this._modelInfo.id); | ||
| } | ||
| } | ||
| //# sourceMappingURL=modelVariant.js.map |
| import { FoundryLocalConfig } from './configuration.js'; | ||
| import { Catalog } from './catalog.js'; | ||
| import { ResponsesClient } from './openai/responsesClient.js'; | ||
| import { EpInfo, EpDownloadResult } from './types.js'; | ||
| /** | ||
| * The main entry point for the Foundry Local SDK. | ||
| * Manages the initialization of the core system and provides access to the Catalog and ModelLoadManager. | ||
| */ | ||
| export declare class FoundryLocalManager { | ||
| private static instance; | ||
| private static pendingCreate?; | ||
| private config; | ||
| private coreInterop; | ||
| private _modelLoadManager; | ||
| private _catalog; | ||
| private _urls; | ||
| private constructor(); | ||
| private initializeSync; | ||
| private initializeAsync; | ||
| /** | ||
| * Creates the FoundryLocalManager singleton with the provided configuration. | ||
| * Note: This method blocks the event loop during initialization. | ||
| * For non-blocking initialization, use {@link createAsync} instead. | ||
| * @param config - The configuration settings for the SDK (plain object). | ||
| * @returns The initialized FoundryLocalManager instance. | ||
| * @example | ||
| * ```typescript | ||
| * const manager = FoundryLocalManager.create({ | ||
| * appName: 'MyApp', | ||
| * logLevel: 'info' | ||
| * }); | ||
| * ``` | ||
| */ | ||
| static create(config: FoundryLocalConfig): FoundryLocalManager; | ||
| /** | ||
| * Creates the FoundryLocalManager singleton with the provided configuration. | ||
| * Native command execution is performed asynchronously to avoid blocking the | ||
| * event loop during initialization. Note that some synchronous setup (e.g., | ||
| * loading the native addon) still occurs before the first await. | ||
| * @param config - The configuration settings for the SDK (plain object). | ||
| * @returns A promise that resolves to the initialized FoundryLocalManager instance. | ||
| * @example | ||
| * ```typescript | ||
| * const manager = await FoundryLocalManager.createAsync({ | ||
| * appName: 'MyApp', | ||
| * logLevel: 'info' | ||
| * }); | ||
| * ``` | ||
| */ | ||
| static createAsync(config: FoundryLocalConfig): Promise<FoundryLocalManager>; | ||
| /** | ||
| * Gets the Catalog instance for discovering and managing models. | ||
| * @returns The Catalog instance. | ||
| */ | ||
| get catalog(): Catalog; | ||
| /** | ||
| * Gets the URLs where the web service is listening. | ||
| * Returns an empty array if the web service is not running. | ||
| * @returns An array of URLs. | ||
| */ | ||
| get urls(): string[]; | ||
| /** | ||
| * Starts the local web service. | ||
| * Use the `urls` property to retrieve the bound addresses after the service has started. | ||
| * If no listener address is configured, the service defaults to `127.0.0.1:0` (binding to a random ephemeral port). | ||
| * @throws Error - If starting the service fails. | ||
| */ | ||
| startWebService(): void; | ||
| /** | ||
| * Stops the local web service. | ||
| * @throws Error - If stopping the service fails. | ||
| */ | ||
| stopWebService(): void; | ||
| /** | ||
| * Whether the web service is currently running. | ||
| */ | ||
| get isWebServiceRunning(): boolean; | ||
| /** | ||
| * Discovers available execution providers (EPs) and their registration status. | ||
| * @returns An array of EpInfo describing each available EP. | ||
| */ | ||
| discoverEps(): EpInfo[]; | ||
| /** | ||
| * Downloads and registers execution providers. | ||
| * @returns A promise that resolves with an EpDownloadResult describing the outcome. | ||
| */ | ||
| downloadAndRegisterEps(): Promise<EpDownloadResult>; | ||
| /** | ||
| * Downloads and registers execution providers. | ||
| * @param signal - Optional AbortSignal used to cancel an in-progress download. | ||
| * @returns A promise that resolves with an EpDownloadResult describing the outcome. | ||
| */ | ||
| downloadAndRegisterEps(signal: AbortSignal): Promise<EpDownloadResult>; | ||
| /** | ||
| * Downloads and registers execution providers. | ||
| * @param names - Array of EP names to download. | ||
| * @returns A promise that resolves with an EpDownloadResult describing the outcome. | ||
| */ | ||
| downloadAndRegisterEps(names: string[]): Promise<EpDownloadResult>; | ||
| /** | ||
| * Downloads and registers execution providers. | ||
| * @param names - Array of EP names to download. | ||
| * @param signal - Optional AbortSignal used to cancel an in-progress download. | ||
| * @returns A promise that resolves with an EpDownloadResult describing the outcome. | ||
| */ | ||
| downloadAndRegisterEps(names: string[], signal: AbortSignal): Promise<EpDownloadResult>; | ||
| /** | ||
| * Downloads and registers execution providers, reporting progress. | ||
| * @param progressCallback - Callback invoked with (epName, percent) as each EP downloads. Percent is 0-100. | ||
| * @returns A promise that resolves with an EpDownloadResult describing the outcome. | ||
| */ | ||
| downloadAndRegisterEps(progressCallback: (epName: string, percent: number) => void): Promise<EpDownloadResult>; | ||
| /** | ||
| * Downloads and registers execution providers, reporting progress. | ||
| * @param progressCallback - Callback invoked with (epName, percent) as each EP downloads. Percent is 0-100. | ||
| * @param signal - Optional AbortSignal used to cancel an in-progress download. | ||
| * @returns A promise that resolves with an EpDownloadResult describing the outcome. | ||
| */ | ||
| downloadAndRegisterEps(progressCallback: (epName: string, percent: number) => void, signal: AbortSignal): Promise<EpDownloadResult>; | ||
| /** | ||
| * Downloads and registers execution providers, reporting progress. | ||
| * @param names - Array of EP names to download. | ||
| * @param progressCallback - Callback invoked with (epName, percent) as each EP downloads. Percent is 0-100. | ||
| * @returns A promise that resolves with an EpDownloadResult describing the outcome. | ||
| */ | ||
| downloadAndRegisterEps(names: string[], progressCallback: (epName: string, percent: number) => void): Promise<EpDownloadResult>; | ||
| /** | ||
| * Downloads and registers execution providers, reporting progress. | ||
| * @param names - Array of EP names to download. | ||
| * @param progressCallback - Callback invoked with (epName, percent) as each EP downloads. Percent is 0-100. | ||
| * @param signal - Optional AbortSignal used to cancel an in-progress download. | ||
| * @returns A promise that resolves with an EpDownloadResult describing the outcome. | ||
| */ | ||
| downloadAndRegisterEps(names: string[], progressCallback: (epName: string, percent: number) => void, signal: AbortSignal): Promise<EpDownloadResult>; | ||
| /** | ||
| * Downloads and registers execution providers, preserving compatibility with callers that pass undefined for names. | ||
| * @param names - Undefined to download all EPs. | ||
| * @param signal - Optional AbortSignal used to cancel an in-progress download. | ||
| * @returns A promise that resolves with an EpDownloadResult describing the outcome. | ||
| */ | ||
| downloadAndRegisterEps(names: undefined, signal: AbortSignal): Promise<EpDownloadResult>; | ||
| /** | ||
| * Downloads and registers execution providers, preserving compatibility with callers that pass undefined for names. | ||
| * @param names - Undefined to download all EPs. | ||
| * @param progressCallback - Callback invoked with (epName, percent) as each EP downloads. Percent is 0-100. | ||
| * @param signal - Optional AbortSignal used to cancel an in-progress download. | ||
| * @returns A promise that resolves with an EpDownloadResult describing the outcome. | ||
| */ | ||
| downloadAndRegisterEps(names: undefined, progressCallback: (epName: string, percent: number) => void, signal?: AbortSignal): Promise<EpDownloadResult>; | ||
| /** | ||
| * Creates a ResponsesClient for interacting with the Responses API. | ||
| * The web service must be started first via `startWebService()`. | ||
| * @param modelId - Optional default model ID for requests. | ||
| * @returns A ResponsesClient instance. | ||
| * @throws Error - If the web service is not running. | ||
| */ | ||
| createResponsesClient(modelId?: string): ResponsesClient; | ||
| } |
| import { Configuration } from './configuration.js'; | ||
| import { CoreInterop } from './detail/coreInterop.js'; | ||
| import { ModelLoadManager } from './detail/modelLoadManager.js'; | ||
| import { Catalog } from './catalog.js'; | ||
| import { ResponsesClient } from './openai/responsesClient.js'; | ||
| function isAbortSignal(value) { | ||
| return typeof value === 'object' | ||
| && value !== null | ||
| && 'aborted' in value | ||
| && typeof value.aborted === 'boolean'; | ||
| } | ||
| /** | ||
| * The main entry point for the Foundry Local SDK. | ||
| * Manages the initialization of the core system and provides access to the Catalog and ModelLoadManager. | ||
| */ | ||
| export class FoundryLocalManager { | ||
| static instance; | ||
| static pendingCreate; | ||
| config; | ||
| coreInterop; | ||
| _modelLoadManager; | ||
| _catalog; | ||
| _urls = []; | ||
| constructor(config) { | ||
| this.config = config; | ||
| this.coreInterop = new CoreInterop(this.config); | ||
| this._modelLoadManager = new ModelLoadManager(this.coreInterop); | ||
| } | ||
| initializeSync() { | ||
| this.coreInterop.executeCommand("initialize", { Params: this.config.params }); | ||
| const catalogName = this.coreInterop.executeCommand("get_catalog_name"); | ||
| this._catalog = new Catalog(this.coreInterop, this._modelLoadManager, catalogName); | ||
| } | ||
| async initializeAsync() { | ||
| await this.coreInterop.executeCommandAsync("initialize", { Params: this.config.params }); | ||
| const catalogName = await this.coreInterop.executeCommandAsync("get_catalog_name"); | ||
| this._catalog = new Catalog(this.coreInterop, this._modelLoadManager, catalogName); | ||
| } | ||
| /** | ||
| * Creates the FoundryLocalManager singleton with the provided configuration. | ||
| * Note: This method blocks the event loop during initialization. | ||
| * For non-blocking initialization, use {@link createAsync} instead. | ||
| * @param config - The configuration settings for the SDK (plain object). | ||
| * @returns The initialized FoundryLocalManager instance. | ||
| * @example | ||
| * ```typescript | ||
| * const manager = FoundryLocalManager.create({ | ||
| * appName: 'MyApp', | ||
| * logLevel: 'info' | ||
| * }); | ||
| * ``` | ||
| */ | ||
| static create(config) { | ||
| if (!FoundryLocalManager.instance) { | ||
| if (FoundryLocalManager.pendingCreate) { | ||
| throw new Error("FoundryLocalManager.createAsync() is in progress. " + | ||
| "Await that call instead of invoking create()."); | ||
| } | ||
| const internalConfig = new Configuration(config); | ||
| const manager = new FoundryLocalManager(internalConfig); | ||
| manager.initializeSync(); | ||
| FoundryLocalManager.instance = manager; | ||
| } | ||
| return FoundryLocalManager.instance; | ||
| } | ||
| /** | ||
| * Creates the FoundryLocalManager singleton with the provided configuration. | ||
| * Native command execution is performed asynchronously to avoid blocking the | ||
| * event loop during initialization. Note that some synchronous setup (e.g., | ||
| * loading the native addon) still occurs before the first await. | ||
| * @param config - The configuration settings for the SDK (plain object). | ||
| * @returns A promise that resolves to the initialized FoundryLocalManager instance. | ||
| * @example | ||
| * ```typescript | ||
| * const manager = await FoundryLocalManager.createAsync({ | ||
| * appName: 'MyApp', | ||
| * logLevel: 'info' | ||
| * }); | ||
| * ``` | ||
| */ | ||
| static createAsync(config) { | ||
| if (FoundryLocalManager.instance) { | ||
| return Promise.resolve(FoundryLocalManager.instance); | ||
| } | ||
| if (!FoundryLocalManager.pendingCreate) { | ||
| const internalConfig = new Configuration(config); | ||
| const manager = new FoundryLocalManager(internalConfig); | ||
| FoundryLocalManager.pendingCreate = manager.initializeAsync() | ||
| .then(() => { | ||
| FoundryLocalManager.instance = manager; | ||
| return manager; | ||
| }) | ||
| .finally(() => { | ||
| FoundryLocalManager.pendingCreate = undefined; | ||
| }); | ||
| } | ||
| return FoundryLocalManager.pendingCreate; | ||
| } | ||
| /** | ||
| * Gets the Catalog instance for discovering and managing models. | ||
| * @returns The Catalog instance. | ||
| */ | ||
| get catalog() { | ||
| return this._catalog; | ||
| } | ||
| /** | ||
| * Gets the URLs where the web service is listening. | ||
| * Returns an empty array if the web service is not running. | ||
| * @returns An array of URLs. | ||
| */ | ||
| get urls() { | ||
| return this._urls; | ||
| } | ||
| /** | ||
| * Starts the local web service. | ||
| * Use the `urls` property to retrieve the bound addresses after the service has started. | ||
| * If no listener address is configured, the service defaults to `127.0.0.1:0` (binding to a random ephemeral port). | ||
| * @throws Error - If starting the service fails. | ||
| */ | ||
| startWebService() { | ||
| const response = this.coreInterop.executeCommand("start_service"); | ||
| try { | ||
| this._urls = JSON.parse(response); | ||
| } | ||
| catch (error) { | ||
| throw new Error(`Failed to decode JSON response from start_service: ${error}. Response was: ${response}`); | ||
| } | ||
| } | ||
| /** | ||
| * Stops the local web service. | ||
| * @throws Error - If stopping the service fails. | ||
| */ | ||
| stopWebService() { | ||
| if (this._urls.length > 0) { | ||
| this.coreInterop.executeCommand("stop_service"); | ||
| this._urls = []; | ||
| } | ||
| } | ||
| /** | ||
| * Whether the web service is currently running. | ||
| */ | ||
| get isWebServiceRunning() { | ||
| return this._urls.length > 0; | ||
| } | ||
| /** | ||
| * Discovers available execution providers (EPs) and their registration status. | ||
| * @returns An array of EpInfo describing each available EP. | ||
| */ | ||
| discoverEps() { | ||
| const response = this.coreInterop.executeCommand("discover_eps"); | ||
| try { | ||
| const raw = JSON.parse(response); | ||
| return raw.map((ep) => ({ | ||
| name: ep.Name, | ||
| isRegistered: ep.IsRegistered | ||
| })); | ||
| } | ||
| catch (error) { | ||
| throw new Error(`Failed to decode JSON response from discover_eps: ${error}. Response was: ${response}`); | ||
| } | ||
| } | ||
| async downloadAndRegisterEps(namesOrCallbackOrSignal, progressCallbackOrSignal, maybeSignal) { | ||
| const names = Array.isArray(namesOrCallbackOrSignal) ? namesOrCallbackOrSignal : undefined; | ||
| const progressCallback = typeof namesOrCallbackOrSignal === 'function' | ||
| ? namesOrCallbackOrSignal | ||
| : typeof progressCallbackOrSignal === 'function' | ||
| ? progressCallbackOrSignal | ||
| : undefined; | ||
| const signal = isAbortSignal(namesOrCallbackOrSignal) | ||
| ? namesOrCallbackOrSignal | ||
| : isAbortSignal(progressCallbackOrSignal) | ||
| ? progressCallbackOrSignal | ||
| : maybeSignal; | ||
| const params = {}; | ||
| if (names && names.length > 0) { | ||
| params.Params = { Names: names.join(",") }; | ||
| } | ||
| let response; | ||
| const commandParams = Object.keys(params).length > 0 ? params : undefined; | ||
| if (!progressCallback && !signal) { | ||
| response = await this.coreInterop.executeCommandAsync("download_and_register_eps", commandParams); | ||
| } | ||
| else if (progressCallback) { | ||
| response = await this.coreInterop.executeCommandStreaming("download_and_register_eps", commandParams, (chunk) => { | ||
| const sepIndex = chunk.indexOf('|'); | ||
| if (sepIndex >= 0) { | ||
| const epName = chunk.substring(0, sepIndex); | ||
| const percent = parseFloat(chunk.substring(sepIndex + 1)); | ||
| if (!isNaN(percent)) { | ||
| progressCallback(epName || '', percent); | ||
| } | ||
| } | ||
| }, signal); | ||
| } | ||
| else { | ||
| response = await this.coreInterop.executeCommandStreaming("download_and_register_eps", commandParams, () => { }, // no-op callback | ||
| signal); | ||
| } | ||
| let epResult; | ||
| try { | ||
| const raw = JSON.parse(response); | ||
| epResult = { | ||
| success: raw.Success, | ||
| status: raw.Status, | ||
| registeredEps: raw.RegisteredEps, | ||
| failedEps: raw.FailedEps | ||
| }; | ||
| } | ||
| catch (error) { | ||
| throw new Error(`Failed to decode JSON response from download_and_register_eps: ${error}. Response was: ${response}`); | ||
| } | ||
| // Invalidate the catalog cache if any EP was newly registered so the next access | ||
| // re-fetches models with the updated set of available EPs. | ||
| if (epResult.success || epResult.registeredEps.length > 0) { | ||
| this._catalog.invalidateCache(); | ||
| } | ||
| return epResult; | ||
| } | ||
| /** | ||
| * Creates a ResponsesClient for interacting with the Responses API. | ||
| * The web service must be started first via `startWebService()`. | ||
| * @param modelId - Optional default model ID for requests. | ||
| * @returns A ResponsesClient instance. | ||
| * @throws Error - If the web service is not running. | ||
| */ | ||
| createResponsesClient(modelId) { | ||
| if (this._urls.length === 0) { | ||
| throw new Error('Web service is not running. Call startWebService() before creating a ResponsesClient.'); | ||
| } | ||
| return new ResponsesClient(this._urls[0], modelId); | ||
| } | ||
| } | ||
| //# sourceMappingURL=foundryLocalManager.js.map |
| import { ChatClient } from './openai/chatClient.js'; | ||
| import { AudioClient } from './openai/audioClient.js'; | ||
| import { EmbeddingClient } from './openai/embeddingClient.js'; | ||
| import { ResponsesClient } from './openai/responsesClient.js'; | ||
| import { ModelInfo } from './types.js'; | ||
| export interface IModel { | ||
| get id(): string; | ||
| get alias(): string; | ||
| get info(): ModelInfo; | ||
| get isCached(): boolean; | ||
| isLoaded(): Promise<boolean>; | ||
| get contextLength(): number | null; | ||
| get inputModalities(): string | null; | ||
| get outputModalities(): string | null; | ||
| get capabilities(): string | null; | ||
| get supportsToolCalling(): boolean | null; | ||
| /** | ||
| * Download the model to local cache if not already present. | ||
| * @param progressCallbackOrSignal - Optional callback for download progress (0-100), or AbortSignal. | ||
| * @param signal - Optional AbortSignal when a progress callback is provided. | ||
| */ | ||
| download(progressCallbackOrSignal?: ((progress: number) => void) | AbortSignal, signal?: AbortSignal): Promise<void>; | ||
| get path(): string; | ||
| load(): Promise<void>; | ||
| removeFromCache(): void; | ||
| unload(): Promise<void>; | ||
| createChatClient(): ChatClient; | ||
| createAudioClient(): AudioClient; | ||
| createEmbeddingClient(): EmbeddingClient; | ||
| /** | ||
| * Creates a ResponsesClient for interacting with the model via the Responses API. | ||
| * Unlike createChatClient/createAudioClient (which use FFI), the Responses API | ||
| * is HTTP-based, so the web service base URL must be provided. | ||
| * @param baseUrl - The base URL of the Foundry Local web service. | ||
| */ | ||
| createResponsesClient(baseUrl: string): ResponsesClient; | ||
| /** | ||
| * Variants of the model that are available. Variants of the model are optimized for different devices. | ||
| */ | ||
| get variants(): IModel[]; | ||
| /** | ||
| * Select a model variant from variants to use for IModel operations. | ||
| * An IModel from `variants` can also be used directly. | ||
| * @param variant - Model variant to select. Must be one of the variants in `variants`. | ||
| * @throws Error if variant is not valid for this model. | ||
| */ | ||
| selectVariant(variant: IModel): void; | ||
| } |
| export {}; | ||
| //# sourceMappingURL=imodel.js.map |
| export { FoundryLocalManager } from './foundryLocalManager.js'; | ||
| export type { FoundryLocalConfig } from './configuration.js'; | ||
| export { Catalog } from './catalog.js'; | ||
| /** @internal */ | ||
| export { Model } from './detail/model.js'; | ||
| /** @internal */ | ||
| export { ModelVariant } from './detail/modelVariant.js'; | ||
| export type { IModel } from './imodel.js'; | ||
| export { ChatClient, ChatClientSettings } from './openai/chatClient.js'; | ||
| export { AudioClient, AudioClientSettings } from './openai/audioClient.js'; | ||
| export { EmbeddingClient } from './openai/embeddingClient.js'; | ||
| export { LiveAudioTranscriptionSession, LiveAudioTranscriptionOptions } from './openai/liveAudioSession.js'; | ||
| export type { LiveAudioTranscriptionResponse, TranscriptionContentPart } from './openai/liveAudioTypes.js'; | ||
| export { ResponsesClient, ResponsesClientSettings, getOutputText } from './openai/responsesClient.js'; | ||
| export { ModelLoadManager } from './detail/modelLoadManager.js'; | ||
| /** @internal */ | ||
| export { CoreInterop } from './detail/coreInterop.js'; | ||
| /** @internal */ | ||
| export { Configuration } from './configuration.js'; | ||
| export * from './types.js'; |
| export { FoundryLocalManager } from './foundryLocalManager.js'; | ||
| export { Catalog } from './catalog.js'; | ||
| /** @internal */ | ||
| export { Model } from './detail/model.js'; | ||
| /** @internal */ | ||
| export { ModelVariant } from './detail/modelVariant.js'; | ||
| export { ChatClient, ChatClientSettings } from './openai/chatClient.js'; | ||
| export { AudioClient, AudioClientSettings } from './openai/audioClient.js'; | ||
| export { EmbeddingClient } from './openai/embeddingClient.js'; | ||
| export { LiveAudioTranscriptionSession, LiveAudioTranscriptionOptions } from './openai/liveAudioSession.js'; | ||
| export { ResponsesClient, ResponsesClientSettings, getOutputText } from './openai/responsesClient.js'; | ||
| export { ModelLoadManager } from './detail/modelLoadManager.js'; | ||
| /** @internal */ | ||
| export { CoreInterop } from './detail/coreInterop.js'; | ||
| /** @internal */ | ||
| export { Configuration } from './configuration.js'; | ||
| export * from './types.js'; | ||
| //# sourceMappingURL=index.js.map |
| import { CoreInterop } from '../detail/coreInterop.js'; | ||
| import { LiveAudioTranscriptionSession } from './liveAudioSession.js'; | ||
| export declare class AudioClientSettings { | ||
| language?: string; | ||
| temperature?: number; | ||
| /** | ||
| * Serializes the settings into an OpenAI-compatible request object. | ||
| * @internal | ||
| */ | ||
| _serialize(): { | ||
| [k: string]: unknown; | ||
| }; | ||
| } | ||
| /** | ||
| * Client for performing audio operations (transcription, translation) with a loaded model. | ||
| * Follows the OpenAI Audio API structure. | ||
| */ | ||
| export declare class AudioClient { | ||
| private modelId; | ||
| private coreInterop; | ||
| /** | ||
| * Configuration settings for audio operations. | ||
| */ | ||
| settings: AudioClientSettings; | ||
| /** | ||
| * @internal | ||
| * Restricted to internal use because CoreInterop is an internal implementation detail. | ||
| * Users should create clients via the Model.createAudioClient() factory method. | ||
| */ | ||
| constructor(modelId: string, coreInterop: CoreInterop); | ||
| /** | ||
| * Creates a LiveAudioTranscriptionSession for real-time audio streaming ASR. | ||
| * @returns A LiveAudioTranscriptionSession instance. | ||
| */ | ||
| createLiveTranscriptionSession(): LiveAudioTranscriptionSession; | ||
| /** | ||
| * Validates that the audio file path is a non-empty string. | ||
| * @internal | ||
| */ | ||
| private validateAudioFilePath; | ||
| /** | ||
| * Transcribes audio into the input language. | ||
| * @param audioFilePath - Path to the audio file to transcribe. | ||
| * @returns The transcription result. | ||
| * @throws Error - If audioFilePath is invalid or transcription fails. | ||
| */ | ||
| transcribe(audioFilePath: string): Promise<any>; | ||
| /** | ||
| * Transcribes audio into the input language using streaming, returning an async iterable of chunks. | ||
| * @param audioFilePath - Path to the audio file to transcribe. | ||
| * @returns An async iterable that yields parsed streaming transcription chunks. | ||
| * @throws Error - If audioFilePath is invalid, or streaming fails. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * for await (const chunk of audioClient.transcribeStreaming('recording.wav')) { | ||
| * process.stdout.write(chunk.text); | ||
| * } | ||
| * ``` | ||
| */ | ||
| transcribeStreaming(audioFilePath: string): AsyncIterable<any>; | ||
| } |
| import { LiveAudioTranscriptionSession } from './liveAudioSession.js'; | ||
| export class AudioClientSettings { | ||
| language; | ||
| temperature; | ||
| /** | ||
| * Serializes the settings into an OpenAI-compatible request object. | ||
| * @internal | ||
| */ | ||
| _serialize() { | ||
| // Standard OpenAI properties | ||
| const result = { | ||
| Language: this.language, | ||
| Temperature: this.temperature, | ||
| }; | ||
| // Foundry specific metadata properties | ||
| const metadata = {}; | ||
| if (this.language !== undefined) { | ||
| metadata["language"] = this.language; | ||
| } | ||
| if (this.temperature !== undefined) { | ||
| metadata["temperature"] = this.temperature.toString(); | ||
| } | ||
| if (Object.keys(metadata).length > 0) { | ||
| result.metadata = metadata; | ||
| } | ||
| // Filter out undefined properties | ||
| return Object.fromEntries(Object.entries(result).filter(([_, v]) => v !== undefined)); | ||
| } | ||
| } | ||
| /** | ||
| * Client for performing audio operations (transcription, translation) with a loaded model. | ||
| * Follows the OpenAI Audio API structure. | ||
| */ | ||
| export class AudioClient { | ||
| modelId; | ||
| coreInterop; | ||
| /** | ||
| * Configuration settings for audio operations. | ||
| */ | ||
| settings = new AudioClientSettings(); | ||
| /** | ||
| * @internal | ||
| * Restricted to internal use because CoreInterop is an internal implementation detail. | ||
| * Users should create clients via the Model.createAudioClient() factory method. | ||
| */ | ||
| constructor(modelId, coreInterop) { | ||
| this.modelId = modelId; | ||
| this.coreInterop = coreInterop; | ||
| } | ||
| /** | ||
| * Creates a LiveAudioTranscriptionSession for real-time audio streaming ASR. | ||
| * @returns A LiveAudioTranscriptionSession instance. | ||
| */ | ||
| createLiveTranscriptionSession() { | ||
| return new LiveAudioTranscriptionSession(this.modelId, this.coreInterop); | ||
| } | ||
| /** | ||
| * Validates that the audio file path is a non-empty string. | ||
| * @internal | ||
| */ | ||
| validateAudioFilePath(audioFilePath) { | ||
| if (typeof audioFilePath !== 'string' || audioFilePath.trim() === '') { | ||
| throw new Error('Audio file path must be a non-empty string.'); | ||
| } | ||
| } | ||
| /** | ||
| * Transcribes audio into the input language. | ||
| * @param audioFilePath - Path to the audio file to transcribe. | ||
| * @returns The transcription result. | ||
| * @throws Error - If audioFilePath is invalid or transcription fails. | ||
| */ | ||
| async transcribe(audioFilePath) { | ||
| this.validateAudioFilePath(audioFilePath); | ||
| const request = { | ||
| Model: this.modelId, | ||
| FileName: audioFilePath, | ||
| ...this.settings._serialize() | ||
| }; | ||
| try { | ||
| const response = this.coreInterop.executeCommand("audio_transcribe", { Params: { OpenAICreateRequest: JSON.stringify(request) } }); | ||
| return JSON.parse(response); | ||
| } | ||
| catch (error) { | ||
| throw new Error(`Audio transcription failed for model '${this.modelId}': ${error instanceof Error ? error.message : String(error)}`, { cause: error }); | ||
| } | ||
| } | ||
| /** | ||
| * Transcribes audio into the input language using streaming, returning an async iterable of chunks. | ||
| * @param audioFilePath - Path to the audio file to transcribe. | ||
| * @returns An async iterable that yields parsed streaming transcription chunks. | ||
| * @throws Error - If audioFilePath is invalid, or streaming fails. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * for await (const chunk of audioClient.transcribeStreaming('recording.wav')) { | ||
| * process.stdout.write(chunk.text); | ||
| * } | ||
| * ``` | ||
| */ | ||
| transcribeStreaming(audioFilePath) { | ||
| this.validateAudioFilePath(audioFilePath); | ||
| const request = { | ||
| Model: this.modelId, | ||
| FileName: audioFilePath, | ||
| ...this.settings._serialize() | ||
| }; | ||
| // Capture instance properties to local variables because `this` is not | ||
| // accessible inside the [Symbol.asyncIterator]() method below — it's a | ||
| // regular method on the returned object literal, not on the AudioClient. | ||
| const coreInterop = this.coreInterop; | ||
| const modelId = this.modelId; | ||
| // Return an AsyncIterable object. The [Symbol.asyncIterator]() factory | ||
| // is called once when the consumer starts a `for await` loop, and it | ||
| // returns the AsyncIterator (with next() / return() methods). | ||
| return { | ||
| [Symbol.asyncIterator]() { | ||
| // Buffer for chunks received from the native callback. | ||
| // Uses a head index for O(1) dequeue instead of Array.shift() which is O(n). | ||
| // JavaScript's single-threaded event loop ensures no race conditions | ||
| // between the callback pushing chunks and next() consuming them. | ||
| const chunks = []; | ||
| let head = 0; | ||
| let done = false; | ||
| let cancelled = false; | ||
| let error = null; | ||
| let resolve = null; | ||
| let nextInFlight = false; | ||
| const streamingPromise = coreInterop.executeCommandStreaming("audio_transcribe", { Params: { OpenAICreateRequest: JSON.stringify(request) } }, (chunkStr) => { | ||
| if (cancelled || error) | ||
| return; | ||
| if (chunkStr) { | ||
| try { | ||
| const chunk = JSON.parse(chunkStr); | ||
| chunks.push(chunk); | ||
| } | ||
| catch (e) { | ||
| if (!error) { | ||
| error = new Error(`Failed to parse streaming chunk: ${e instanceof Error ? e.message : String(e)}`, { cause: e }); | ||
| } | ||
| } | ||
| } | ||
| // Wake up any waiting next() call | ||
| if (resolve) { | ||
| const r = resolve; | ||
| resolve = null; | ||
| r(); | ||
| } | ||
| } | ||
| // When the native stream completes, mark done and wake up any | ||
| // pending next() call so it can see that iteration has ended. | ||
| ).then(() => { | ||
| done = true; | ||
| if (resolve) { | ||
| const r = resolve; | ||
| resolve = null; | ||
| r(); // resolve the pending next() promise | ||
| } | ||
| }).catch((err) => { | ||
| if (!error) { | ||
| const underlyingError = err instanceof Error ? err : new Error(String(err)); | ||
| error = new Error(`Streaming audio transcription failed for model '${modelId}': ${underlyingError.message}`, { cause: underlyingError }); | ||
| } | ||
| done = true; | ||
| if (resolve) { | ||
| const r = resolve; | ||
| resolve = null; | ||
| r(); | ||
| } | ||
| }); | ||
| // Return the AsyncIterator object consumed by `for await`. | ||
| // next() yields buffered chunks one at a time; return() is | ||
| // called automatically when the consumer breaks out early. | ||
| return { | ||
| async next() { | ||
| if (nextInFlight) { | ||
| throw new Error('next() called concurrently on streaming iterator; await each call before invoking next().'); | ||
| } | ||
| nextInFlight = true; | ||
| try { | ||
| while (true) { | ||
| if (head < chunks.length) { | ||
| const value = chunks[head]; | ||
| chunks[head] = undefined; // allow GC | ||
| head++; | ||
| // Compact the array when all buffered chunks have been consumed | ||
| if (head === chunks.length) { | ||
| chunks.length = 0; | ||
| head = 0; | ||
| } | ||
| return { value, done: false }; | ||
| } | ||
| if (error) { | ||
| throw error; | ||
| } | ||
| if (done || cancelled) { | ||
| return { value: undefined, done: true }; | ||
| } | ||
| // Wait for the next chunk or completion | ||
| await new Promise((r) => { resolve = r; }); | ||
| } | ||
| } | ||
| finally { | ||
| nextInFlight = false; | ||
| } | ||
| }, | ||
| async return() { | ||
| // Mark cancelled so the callback stops buffering. | ||
| // Note: the underlying native stream cannot be cancelled | ||
| // (CoreInterop.executeCommandStreaming has no abort support), | ||
| // so the koffi callback may still fire but will no-op due | ||
| // to the cancelled guard above. | ||
| cancelled = true; | ||
| chunks.length = 0; | ||
| head = 0; | ||
| if (resolve) { | ||
| const r = resolve; | ||
| resolve = null; | ||
| r(); | ||
| } | ||
| return { value: undefined, done: true }; | ||
| } | ||
| }; | ||
| } | ||
| }; | ||
| } | ||
| } | ||
| //# sourceMappingURL=audioClient.js.map |
| import { CoreInterop } from '../detail/coreInterop.js'; | ||
| import { ResponseFormat, ToolChoice } from '../types.js'; | ||
| export declare class ChatClientSettings { | ||
| frequencyPenalty?: number; | ||
| maxTokens?: number; | ||
| n?: number; | ||
| temperature?: number; | ||
| presencePenalty?: number; | ||
| randomSeed?: number; | ||
| topK?: number; | ||
| topP?: number; | ||
| responseFormat?: ResponseFormat; | ||
| toolChoice?: ToolChoice; | ||
| /** | ||
| * Serializes the settings into an OpenAI-compatible request object. | ||
| * @internal | ||
| */ | ||
| _serialize(): any; | ||
| /** | ||
| * Validates that the provided ResponseFormat object is well-formed. | ||
| * @internal | ||
| * @param format | ||
| */ | ||
| private validateResponseFormat; | ||
| /** | ||
| * Validates that the provided ToolChoice object is well-formed. | ||
| * @internal | ||
| * @param choice | ||
| */ | ||
| private validateToolChoice; | ||
| } | ||
| /** | ||
| * Client for performing chat completions with a loaded model. | ||
| * Follows the OpenAI Chat Completion API structure. | ||
| */ | ||
| export declare class ChatClient { | ||
| private modelId; | ||
| private coreInterop; | ||
| /** | ||
| * Configuration settings for chat completions. | ||
| */ | ||
| settings: ChatClientSettings; | ||
| /** | ||
| * @internal | ||
| * Restricted to internal use because CoreInterop is an internal implementation detail. | ||
| * Users should create clients via the Model.createChatClient() factory method. | ||
| */ | ||
| constructor(modelId: string, coreInterop: CoreInterop); | ||
| /** | ||
| * Validates that messages array is properly formed. | ||
| * @internal | ||
| */ | ||
| private validateMessages; | ||
| /** | ||
| * Validates that tools array is properly formed. | ||
| * @internal | ||
| */ | ||
| private validateTools; | ||
| /** | ||
| * Performs a synchronous chat completion. | ||
| * @param messages - An array of message objects (e.g., { role: 'user', content: 'Hello' }). | ||
| * @param tools - An array of tool objects (e.g. { type: 'function', function: { name: 'get_apps', description: 'Returns a list of apps available on the system' } }). | ||
| * @returns The chat completion response object. | ||
| * @throws Error - If messages or tools are invalid or completion fails. | ||
| */ | ||
| completeChat(messages: any[]): Promise<any>; | ||
| completeChat(messages: any[], tools: any[]): Promise<any>; | ||
| /** | ||
| * Performs a streaming chat completion, returning an async iterable of chunks. | ||
| * @param messages - An array of message objects. | ||
| * @param tools - An optional array of tool objects. | ||
| * @returns An async iterable that yields parsed streaming response chunks. | ||
| * @throws Error - If messages or tools are invalid, or streaming fails. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * // Without tools: | ||
| * for await (const chunk of chatClient.completeStreamingChat(messages)) { | ||
| * const content = chunk.choices?.[0]?.delta?.content; | ||
| * if (content) process.stdout.write(content); | ||
| * } | ||
| * | ||
| * // With tools: | ||
| * for await (const chunk of chatClient.completeStreamingChat(messages, tools)) { | ||
| * const content = chunk.choices?.[0]?.delta?.content; | ||
| * if (content) process.stdout.write(content); | ||
| * } | ||
| * ``` | ||
| */ | ||
| completeStreamingChat(messages: any[]): AsyncIterable<any>; | ||
| completeStreamingChat(messages: any[], tools: any[]): AsyncIterable<any>; | ||
| } |
| export class ChatClientSettings { | ||
| frequencyPenalty; | ||
| maxTokens; | ||
| n; | ||
| temperature; | ||
| presencePenalty; | ||
| randomSeed; | ||
| topK; | ||
| topP; | ||
| responseFormat; | ||
| toolChoice; | ||
| /** | ||
| * Serializes the settings into an OpenAI-compatible request object. | ||
| * @internal | ||
| */ | ||
| _serialize() { | ||
| // Run internal validations | ||
| this.validateResponseFormat(this.responseFormat); | ||
| this.validateToolChoice(this.toolChoice); | ||
| // Helper function to filter out undefined properties from objects | ||
| const filterUndefined = (obj) => { | ||
| return Object.fromEntries(Object.entries(obj).filter(([_, v]) => v !== undefined)); | ||
| }; | ||
| // Standard OpenAI properties | ||
| const result = { | ||
| frequency_penalty: this.frequencyPenalty, | ||
| max_tokens: this.maxTokens, | ||
| n: this.n, | ||
| presence_penalty: this.presencePenalty, | ||
| temperature: this.temperature, | ||
| top_p: this.topP, | ||
| response_format: this.responseFormat ? filterUndefined(this.responseFormat) : undefined, | ||
| tool_choice: this.toolChoice ? filterUndefined(this.toolChoice) : undefined | ||
| }; | ||
| // Foundry specific metadata properties | ||
| const metadata = {}; | ||
| if (this.topK !== undefined) { | ||
| metadata["top_k"] = this.topK.toString(); | ||
| } | ||
| if (this.randomSeed !== undefined) { | ||
| metadata["random_seed"] = this.randomSeed.toString(); | ||
| } | ||
| if (Object.keys(metadata).length > 0) { | ||
| result.metadata = metadata; | ||
| } | ||
| // Filter out undefined properties | ||
| return filterUndefined(result); | ||
| } | ||
| /** | ||
| * Validates that the provided ResponseFormat object is well-formed. | ||
| * @internal | ||
| * @param format | ||
| */ | ||
| validateResponseFormat(format) { | ||
| if (!format) | ||
| return; | ||
| const validTypes = ['text', 'json_object', 'json_schema', 'lark_grammar']; | ||
| if (!validTypes.includes(format.type)) { | ||
| throw new Error(`ResponseFormat type must be one of: ${validTypes.join(', ')}`); | ||
| } | ||
| const validGrammarTypes = ['json_schema', 'lark_grammar']; | ||
| if (validGrammarTypes.includes(format.type)) { | ||
| if (format.type === 'json_schema' && (typeof format.jsonSchema !== 'string' || format.jsonSchema.trim() === '')) { | ||
| throw new Error('ResponseFormat with type "json_schema" must have a valid jsonSchema string.'); | ||
| } | ||
| if (format.type === 'lark_grammar' && (typeof format.larkGrammar !== 'string' || format.larkGrammar.trim() === '')) { | ||
| throw new Error('ResponseFormat with type "lark_grammar" must have a valid larkGrammar string.'); | ||
| } | ||
| } | ||
| else if (format.jsonSchema || format.larkGrammar) { | ||
| throw new Error(`ResponseFormat with type "${format.type}" should not have jsonSchema or larkGrammar properties.`); | ||
| } | ||
| } | ||
| /** | ||
| * Validates that the provided ToolChoice object is well-formed. | ||
| * @internal | ||
| * @param choice | ||
| */ | ||
| validateToolChoice(choice) { | ||
| if (!choice) | ||
| return; | ||
| const validTypes = ['none', 'auto', 'required', 'function']; | ||
| if (!validTypes.includes(choice.type)) { | ||
| throw new Error(`ToolChoice type must be one of: ${validTypes.join(', ')}`); | ||
| } | ||
| if (choice.type === 'function' && (typeof choice.name !== 'string' || choice.name.trim() === '')) { | ||
| throw new Error('ToolChoice with type "function" must have a valid name string.'); | ||
| } | ||
| else if (choice.type !== 'function' && choice.name) { | ||
| throw new Error(`ToolChoice with type "${choice.type}" should not have a name property.`); | ||
| } | ||
| } | ||
| } | ||
| /** | ||
| * Client for performing chat completions with a loaded model. | ||
| * Follows the OpenAI Chat Completion API structure. | ||
| */ | ||
| export class ChatClient { | ||
| modelId; | ||
| coreInterop; | ||
| /** | ||
| * Configuration settings for chat completions. | ||
| */ | ||
| settings = new ChatClientSettings(); | ||
| /** | ||
| * @internal | ||
| * Restricted to internal use because CoreInterop is an internal implementation detail. | ||
| * Users should create clients via the Model.createChatClient() factory method. | ||
| */ | ||
| constructor(modelId, coreInterop) { | ||
| this.modelId = modelId; | ||
| this.coreInterop = coreInterop; | ||
| } | ||
| /** | ||
| * Validates that messages array is properly formed. | ||
| * @internal | ||
| */ | ||
| validateMessages(messages) { | ||
| if (!messages || !Array.isArray(messages) || messages.length === 0) { | ||
| throw new Error('Messages array cannot be null, undefined, or empty.'); | ||
| } | ||
| for (const msg of messages) { | ||
| if (!msg || typeof msg !== 'object' || Array.isArray(msg)) { | ||
| throw new Error('Each message must be a non-null object with both "role" and "content" properties.'); | ||
| } | ||
| if (typeof msg.role !== 'string' || msg.role.trim() === '') { | ||
| throw new Error('Each message must have a "role" property that is a non-empty string.'); | ||
| } | ||
| if (typeof msg.content !== 'string' || msg.content.trim() === '') { | ||
| throw new Error('Each message must have a "content" property that is a non-empty string.'); | ||
| } | ||
| } | ||
| } | ||
| /** | ||
| * Validates that tools array is properly formed. | ||
| * @internal | ||
| */ | ||
| validateTools(tools) { | ||
| if (!tools) | ||
| return; // tools are optional | ||
| if (!Array.isArray(tools)) { | ||
| throw new Error('Tools must be an array if provided.'); | ||
| } | ||
| for (const tool of tools) { | ||
| if (!tool || typeof tool !== 'object' || Array.isArray(tool)) { | ||
| throw new Error('Each tool must be a non-null object with a valid "type" and "function" definition.'); | ||
| } | ||
| if (typeof tool.type !== 'string' || tool.type.trim() === '') { | ||
| throw new Error('Each tool must have a "type" property that is a non-empty string.'); | ||
| } | ||
| if (!tool.function || typeof tool.function !== 'object') { | ||
| throw new Error('Each tool must have a "function" property that is a non-empty object.'); | ||
| } | ||
| if (typeof tool.function.name !== 'string' || tool.function.name.trim() === '') { | ||
| throw new Error('Each tool\'s function must have a "name" property that is a non-empty string.'); | ||
| } | ||
| if (tool.function.description !== undefined && typeof tool.function.description !== 'string') { | ||
| throw new Error('Each tool\'s function "description", if provided, must be a string.'); | ||
| } | ||
| } | ||
| } | ||
| async completeChat(messages, tools) { | ||
| this.validateMessages(messages); | ||
| this.validateTools(tools); | ||
| const request = { | ||
| model: this.modelId, | ||
| messages, | ||
| ...(tools ? { tools } : {}), | ||
| // stream is undefined (false) by default | ||
| ...this.settings._serialize() | ||
| }; | ||
| try { | ||
| const response = this.coreInterop.executeCommand('chat_completions', { | ||
| Params: { OpenAICreateRequest: JSON.stringify(request) } | ||
| }); | ||
| return JSON.parse(response); | ||
| } | ||
| catch (error) { | ||
| throw new Error(`Chat completion failed for model '${this.modelId}': ${error instanceof Error ? error.message : String(error)}`, { cause: error }); | ||
| } | ||
| } | ||
| completeStreamingChat(messages, tools) { | ||
| this.validateMessages(messages); | ||
| this.validateTools(tools); | ||
| const request = { | ||
| model: this.modelId, | ||
| messages, | ||
| ...(tools ? { tools } : {}), | ||
| stream: true, | ||
| ...this.settings._serialize() | ||
| }; | ||
| // Capture instance properties to local variables because `this` is not | ||
| // accessible inside the [Symbol.asyncIterator]() method below — it's a | ||
| // regular method on the returned object literal, not on the ChatClient. | ||
| const coreInterop = this.coreInterop; | ||
| const modelId = this.modelId; | ||
| // Return an AsyncIterable object. The [Symbol.asyncIterator]() factory | ||
| // is called once when the consumer starts a `for await` loop, and it | ||
| // returns the AsyncIterator (with next() / return() methods). | ||
| return { | ||
| [Symbol.asyncIterator]() { | ||
| // Buffer for chunks received from the native callback. | ||
| // Uses a head index for O(1) dequeue instead of Array.shift() which is O(n). | ||
| // JavaScript's single-threaded event loop ensures no race conditions | ||
| // between the callback pushing chunks and next() consuming them. | ||
| const chunks = []; | ||
| let head = 0; | ||
| let done = false; | ||
| let cancelled = false; | ||
| let error = null; | ||
| let resolve = null; | ||
| let nextInFlight = false; | ||
| const streamingPromise = coreInterop.executeCommandStreaming('chat_completions', { Params: { OpenAICreateRequest: JSON.stringify(request) } }, (chunkStr) => { | ||
| if (cancelled || error) | ||
| return; | ||
| if (chunkStr) { | ||
| try { | ||
| const chunk = JSON.parse(chunkStr); | ||
| chunks.push(chunk); | ||
| } | ||
| catch (e) { | ||
| if (!error) { | ||
| error = new Error(`Failed to parse streaming chunk: ${e instanceof Error ? e.message : String(e)}`, { cause: e }); | ||
| } | ||
| } | ||
| } | ||
| // Wake up any waiting next() call | ||
| if (resolve) { | ||
| const r = resolve; | ||
| resolve = null; | ||
| r(); | ||
| } | ||
| } | ||
| // When the native stream completes, mark done and wake up any | ||
| // pending next() call so it can see that iteration has ended. | ||
| ).then(() => { | ||
| done = true; | ||
| if (resolve) { | ||
| const r = resolve; | ||
| resolve = null; | ||
| r(); // resolve the pending next() promise | ||
| } | ||
| }).catch((err) => { | ||
| if (!error) { | ||
| const underlyingError = err instanceof Error ? err : new Error(String(err)); | ||
| error = new Error(`Streaming chat completion failed for model '${modelId}': ${underlyingError.message}`, { cause: underlyingError }); | ||
| } | ||
| done = true; | ||
| if (resolve) { | ||
| const r = resolve; | ||
| resolve = null; | ||
| r(); | ||
| } | ||
| }); | ||
| // Return the AsyncIterator object consumed by `for await`. | ||
| // next() yields buffered chunks one at a time; return() is | ||
| // called automatically when the consumer breaks out early. | ||
| return { | ||
| async next() { | ||
| if (nextInFlight) { | ||
| throw new Error('next() called concurrently on streaming iterator; await each call before invoking next().'); | ||
| } | ||
| nextInFlight = true; | ||
| try { | ||
| while (true) { | ||
| if (head < chunks.length) { | ||
| const value = chunks[head]; | ||
| chunks[head] = undefined; // allow GC | ||
| head++; | ||
| // Compact the array when all buffered chunks have been consumed | ||
| if (head === chunks.length) { | ||
| chunks.length = 0; | ||
| head = 0; | ||
| } | ||
| return { value, done: false }; | ||
| } | ||
| if (error) { | ||
| throw error; | ||
| } | ||
| if (done || cancelled) { | ||
| return { value: undefined, done: true }; | ||
| } | ||
| // Wait for the next chunk or completion | ||
| await new Promise((r) => { resolve = r; }); | ||
| } | ||
| } | ||
| finally { | ||
| nextInFlight = false; | ||
| } | ||
| }, | ||
| async return() { | ||
| // Mark cancelled so the callback stops buffering. | ||
| // Note: the underlying native stream cannot be cancelled | ||
| // (CoreInterop.executeCommandStreaming has no abort support), | ||
| // so the koffi callback may still fire but will no-op due | ||
| // to the cancelled guard above. | ||
| cancelled = true; | ||
| chunks.length = 0; | ||
| head = 0; | ||
| if (resolve) { | ||
| const r = resolve; | ||
| resolve = null; | ||
| r(); | ||
| } | ||
| return { value: undefined, done: true }; | ||
| } | ||
| }; | ||
| } | ||
| }; | ||
| } | ||
| } | ||
| //# sourceMappingURL=chatClient.js.map |
| import { CoreInterop } from '../detail/coreInterop.js'; | ||
| /** | ||
| * Client for generating text embeddings with a loaded model. | ||
| * Follows the OpenAI Embeddings API structure. | ||
| */ | ||
| export declare class EmbeddingClient { | ||
| private modelId; | ||
| private coreInterop; | ||
| /** | ||
| * @internal | ||
| * Restricted to internal use because CoreInterop is an internal implementation detail. | ||
| * Users should create clients via the Model.createEmbeddingClient() factory method. | ||
| */ | ||
| constructor(modelId: string, coreInterop: CoreInterop); | ||
| /** | ||
| * Validates that the input text is a non-empty string. | ||
| * @internal | ||
| */ | ||
| private validateInput; | ||
| /** | ||
| * Validates that the inputs array is non-empty and all elements are non-empty strings. | ||
| * @internal | ||
| */ | ||
| private validateInputs; | ||
| /** | ||
| * Sends an embedding request and parses the response. | ||
| * @internal | ||
| */ | ||
| private executeRequest; | ||
| /** | ||
| * Generates embeddings for the given input text. | ||
| * @param input - The text to generate embeddings for. | ||
| * @returns The embedding response containing the embedding vector. | ||
| */ | ||
| generateEmbedding(input: string): Promise<any>; | ||
| /** | ||
| * Generates embeddings for multiple input texts in a single request. | ||
| * @param inputs - The texts to generate embeddings for. | ||
| * @returns The embedding response containing one embedding vector per input. | ||
| */ | ||
| generateEmbeddings(inputs: string[]): Promise<any>; | ||
| } |
| /** | ||
| * Client for generating text embeddings with a loaded model. | ||
| * Follows the OpenAI Embeddings API structure. | ||
| */ | ||
| export class EmbeddingClient { | ||
| modelId; | ||
| coreInterop; | ||
| /** | ||
| * @internal | ||
| * Restricted to internal use because CoreInterop is an internal implementation detail. | ||
| * Users should create clients via the Model.createEmbeddingClient() factory method. | ||
| */ | ||
| constructor(modelId, coreInterop) { | ||
| this.modelId = modelId; | ||
| this.coreInterop = coreInterop; | ||
| } | ||
| /** | ||
| * Validates that the input text is a non-empty string. | ||
| * @internal | ||
| */ | ||
| validateInput(input) { | ||
| if (typeof input !== 'string' || input.trim() === '') { | ||
| throw new Error('Input must be a non-empty string.'); | ||
| } | ||
| } | ||
| /** | ||
| * Validates that the inputs array is non-empty and all elements are non-empty strings. | ||
| * @internal | ||
| */ | ||
| validateInputs(inputs) { | ||
| if (!inputs || !Array.isArray(inputs) || inputs.length === 0) { | ||
| throw new Error('Inputs must be a non-empty array of strings.'); | ||
| } | ||
| for (const input of inputs) { | ||
| this.validateInput(input); | ||
| } | ||
| } | ||
| /** | ||
| * Sends an embedding request and parses the response. | ||
| * @internal | ||
| */ | ||
| executeRequest(input) { | ||
| const request = { | ||
| model: this.modelId, | ||
| input, | ||
| }; | ||
| try { | ||
| const response = this.coreInterop.executeCommand('embeddings', { | ||
| Params: { OpenAICreateRequest: JSON.stringify(request) } | ||
| }); | ||
| return JSON.parse(response); | ||
| } | ||
| catch (error) { | ||
| throw new Error(`Embedding generation failed for model '${this.modelId}': ${error instanceof Error ? error.message : String(error)}`, { cause: error }); | ||
| } | ||
| } | ||
| /** | ||
| * Generates embeddings for the given input text. | ||
| * @param input - The text to generate embeddings for. | ||
| * @returns The embedding response containing the embedding vector. | ||
| */ | ||
| async generateEmbedding(input) { | ||
| this.validateInput(input); | ||
| return this.executeRequest(input); | ||
| } | ||
| /** | ||
| * Generates embeddings for multiple input texts in a single request. | ||
| * @param inputs - The texts to generate embeddings for. | ||
| * @returns The embedding response containing one embedding vector per input. | ||
| */ | ||
| async generateEmbeddings(inputs) { | ||
| this.validateInputs(inputs); | ||
| return this.executeRequest(inputs); | ||
| } | ||
| } | ||
| //# sourceMappingURL=embeddingClient.js.map |
| import { CoreInterop } from '../detail/coreInterop.js'; | ||
| import { LiveAudioTranscriptionResponse } from './liveAudioTypes.js'; | ||
| /** | ||
| * Audio format settings for a streaming session. | ||
| * Must be configured before calling start(). | ||
| * Settings are frozen once the session starts. | ||
| */ | ||
| export declare class LiveAudioTranscriptionOptions { | ||
| /** PCM sample rate in Hz. Default: 16000. */ | ||
| sampleRate: number; | ||
| /** Number of audio channels. Default: 1 (mono). */ | ||
| channels: number; | ||
| /** Bits per sample. Default: 16. */ | ||
| bitsPerSample: number; | ||
| /** Optional BCP-47 language hint (e.g., "en", "zh"). */ | ||
| language?: string; | ||
| /** Maximum number of audio chunks buffered in the internal push queue. Default: 100. */ | ||
| pushQueueCapacity: number; | ||
| /** @internal Create a frozen copy of these settings. */ | ||
| snapshot(): LiveAudioTranscriptionOptions; | ||
| } | ||
| /** | ||
| * Client for real-time audio streaming ASR (Automatic Speech Recognition). | ||
| * Audio data from a microphone (or other source) is pushed in as PCM chunks, | ||
| * and transcription results are returned as an async iterable. | ||
| * | ||
| * Mirrors the C# LiveAudioTranscriptionSession. | ||
| */ | ||
| export declare class LiveAudioTranscriptionSession { | ||
| private modelId; | ||
| private coreInterop; | ||
| private sessionHandle; | ||
| private started; | ||
| private stopped; | ||
| private outputQueue; | ||
| private pushQueue; | ||
| private pushLoopPromise; | ||
| private activeSettings; | ||
| private sessionAbortController; | ||
| private streamConsumed; | ||
| /** | ||
| * Configuration settings for the streaming session. | ||
| * Must be configured before calling start(). Settings are snapshotted at start(); | ||
| * changes made after start() are ignored for the current session. | ||
| */ | ||
| settings: LiveAudioTranscriptionOptions; | ||
| /** | ||
| * @internal | ||
| * Users should create sessions via AudioClient.createLiveTranscriptionSession(). | ||
| */ | ||
| constructor(modelId: string, coreInterop: CoreInterop); | ||
| /** | ||
| * Start a real-time audio streaming session. | ||
| * Must be called before append() or getStream(). | ||
| * Settings are frozen after this call. | ||
| */ | ||
| start(): Promise<void>; | ||
| /** | ||
| * Push a chunk of raw PCM audio data to the streaming session. | ||
| * Can be called from any context. Chunks are internally queued | ||
| * and serialized to native core one at a time. | ||
| * | ||
| * @param pcmData - Raw PCM audio bytes matching the configured format. | ||
| */ | ||
| append(pcmData: Uint8Array): Promise<void>; | ||
| /** | ||
| * Internal loop that drains the push queue and sends chunks to native core one at a time. | ||
| * Terminates the session on any native error. | ||
| * @internal | ||
| */ | ||
| private pushLoop; | ||
| /** | ||
| * Get the async iterable of transcription results. | ||
| * Results arrive as the native ASR engine processes audio data. | ||
| * | ||
| * Usage: | ||
| * ```ts | ||
| * for await (const result of session.getStream()) { | ||
| * console.log(result.content[0].text); | ||
| * } | ||
| * ``` | ||
| */ | ||
| getStream(): AsyncGenerator<LiveAudioTranscriptionResponse>; | ||
| /** | ||
| * Signal end-of-audio and stop the streaming session. | ||
| * Any remaining buffered audio in the push queue will be drained to native core first. | ||
| * Final results are delivered through getStream() before it completes. | ||
| */ | ||
| stop(): Promise<void>; | ||
| /** | ||
| * Dispose the client and stop any active session. | ||
| * Safe to call multiple times. | ||
| */ | ||
| dispose(): Promise<void>; | ||
| } |
| import { parseTranscriptionResult, tryParseCoreError } from './liveAudioTypes.js'; | ||
| /** | ||
| * Audio format settings for a streaming session. | ||
| * Must be configured before calling start(). | ||
| * Settings are frozen once the session starts. | ||
| */ | ||
| export class LiveAudioTranscriptionOptions { | ||
| /** PCM sample rate in Hz. Default: 16000. */ | ||
| sampleRate = 16000; | ||
| /** Number of audio channels. Default: 1 (mono). */ | ||
| channels = 1; | ||
| /** Bits per sample. Default: 16. */ | ||
| bitsPerSample = 16; | ||
| /** Optional BCP-47 language hint (e.g., "en", "zh"). */ | ||
| language; | ||
| /** Maximum number of audio chunks buffered in the internal push queue. Default: 100. */ | ||
| pushQueueCapacity = 100; | ||
| /** @internal Create a frozen copy of these settings. */ | ||
| snapshot() { | ||
| const copy = new LiveAudioTranscriptionOptions(); | ||
| copy.sampleRate = this.sampleRate; | ||
| copy.channels = this.channels; | ||
| copy.bitsPerSample = this.bitsPerSample; | ||
| copy.language = this.language; | ||
| copy.pushQueueCapacity = this.pushQueueCapacity; | ||
| return Object.freeze(copy); | ||
| } | ||
| } | ||
| /** | ||
| * Internal async queue that acts like C#'s Channel<T>. | ||
| * Supports a single consumer reading via async iteration and multiple producers writing. | ||
| * @internal | ||
| */ | ||
| class AsyncQueue { | ||
| queue = []; | ||
| waitingResolve = null; | ||
| completed = false; | ||
| completionError = null; | ||
| maxCapacity; | ||
| backpressureQueue = []; | ||
| constructor(maxCapacity = Infinity) { | ||
| this.maxCapacity = maxCapacity; | ||
| } | ||
| /** Push an item. If at capacity, waits until space is available. */ | ||
| async write(item) { | ||
| if (this.completed) { | ||
| throw new Error('Cannot write to a completed queue.'); | ||
| } | ||
| if (this.waitingResolve) { | ||
| const resolve = this.waitingResolve; | ||
| this.waitingResolve = null; | ||
| resolve({ value: item, done: false }); | ||
| return; | ||
| } | ||
| while (this.queue.length >= this.maxCapacity) { | ||
| await new Promise((resolve) => { | ||
| this.backpressureQueue.push(resolve); | ||
| }); | ||
| } | ||
| if (this.completed) { | ||
| throw new Error('Cannot write to a completed queue.'); | ||
| } | ||
| this.queue.push(item); | ||
| } | ||
| /** Push an item synchronously (no backpressure wait). Returns false if completed or at capacity. */ | ||
| tryWrite(item) { | ||
| if (this.completed) | ||
| return false; | ||
| if (this.waitingResolve) { | ||
| const resolve = this.waitingResolve; | ||
| this.waitingResolve = null; | ||
| resolve({ value: item, done: false }); | ||
| return true; | ||
| } | ||
| if (this.queue.length >= this.maxCapacity) { | ||
| return false; | ||
| } | ||
| this.queue.push(item); | ||
| return true; | ||
| } | ||
| /** Signal that no more items will be written. */ | ||
| complete(error) { | ||
| if (this.completed) | ||
| return; | ||
| this.completed = true; | ||
| this.completionError = error ?? null; | ||
| // Release all blocked writers | ||
| for (const resolve of this.backpressureQueue) { | ||
| resolve(); | ||
| } | ||
| this.backpressureQueue = []; | ||
| if (this.waitingResolve) { | ||
| const resolve = this.waitingResolve; | ||
| this.waitingResolve = null; | ||
| resolve({ value: undefined, done: true }); | ||
| } | ||
| } | ||
| get error() { | ||
| return this.completionError; | ||
| } | ||
| /** Async iterator for consuming items. */ | ||
| async *[Symbol.asyncIterator]() { | ||
| while (true) { | ||
| if (this.backpressureQueue.length > 0 && this.queue.length < this.maxCapacity) { | ||
| const resolve = this.backpressureQueue.shift(); | ||
| resolve(); | ||
| } | ||
| if (this.queue.length > 0) { | ||
| yield this.queue.shift(); | ||
| continue; | ||
| } | ||
| if (this.completed) { | ||
| if (this.completionError) { | ||
| throw this.completionError; | ||
| } | ||
| return; | ||
| } | ||
| const result = await new Promise((resolve) => { | ||
| this.waitingResolve = resolve; | ||
| }); | ||
| if (result.done) { | ||
| if (this.completionError) { | ||
| throw this.completionError; | ||
| } | ||
| return; | ||
| } | ||
| yield result.value; | ||
| } | ||
| } | ||
| } | ||
| /** | ||
| * Client for real-time audio streaming ASR (Automatic Speech Recognition). | ||
| * Audio data from a microphone (or other source) is pushed in as PCM chunks, | ||
| * and transcription results are returned as an async iterable. | ||
| * | ||
| * Mirrors the C# LiveAudioTranscriptionSession. | ||
| */ | ||
| export class LiveAudioTranscriptionSession { | ||
| modelId; | ||
| coreInterop; | ||
| sessionHandle = null; | ||
| started = false; | ||
| stopped = false; | ||
| outputQueue = null; | ||
| pushQueue = null; | ||
| pushLoopPromise = null; | ||
| activeSettings = null; | ||
| sessionAbortController = null; | ||
| streamConsumed = false; | ||
| /** | ||
| * Configuration settings for the streaming session. | ||
| * Must be configured before calling start(). Settings are snapshotted at start(); | ||
| * changes made after start() are ignored for the current session. | ||
| */ | ||
| settings = new LiveAudioTranscriptionOptions(); | ||
| /** | ||
| * @internal | ||
| * Users should create sessions via AudioClient.createLiveTranscriptionSession(). | ||
| */ | ||
| constructor(modelId, coreInterop) { | ||
| this.modelId = modelId; | ||
| this.coreInterop = coreInterop; | ||
| } | ||
| /** | ||
| * Start a real-time audio streaming session. | ||
| * Must be called before append() or getStream(). | ||
| * Settings are frozen after this call. | ||
| */ | ||
| async start() { | ||
| if (this.started) { | ||
| throw new Error('Streaming session already started. Call stop() first.'); | ||
| } | ||
| this.activeSettings = this.settings.snapshot(); | ||
| this.outputQueue = new AsyncQueue(); | ||
| this.pushQueue = new AsyncQueue(this.activeSettings.pushQueueCapacity); | ||
| this.streamConsumed = false; | ||
| const params = { | ||
| Model: this.modelId, | ||
| SampleRate: this.activeSettings.sampleRate.toString(), | ||
| Channels: this.activeSettings.channels.toString(), | ||
| BitsPerSample: this.activeSettings.bitsPerSample.toString(), | ||
| }; | ||
| if (this.activeSettings.language) { | ||
| params['Language'] = this.activeSettings.language; | ||
| } | ||
| try { | ||
| const response = this.coreInterop.executeCommand("audio_stream_start", { | ||
| Params: params | ||
| }); | ||
| this.sessionHandle = response; | ||
| if (!this.sessionHandle) { | ||
| throw new Error('Native core did not return a session handle.'); | ||
| } | ||
| } | ||
| catch (error) { | ||
| const err = new Error(`Error starting audio stream session: ${error instanceof Error ? error.message : String(error)}`, { cause: error }); | ||
| this.outputQueue.complete(err); | ||
| throw err; | ||
| } | ||
| this.started = true; | ||
| this.stopped = false; | ||
| this.sessionAbortController = new AbortController(); | ||
| this.pushLoopPromise = this.pushLoop(); | ||
| } | ||
| /** | ||
| * Push a chunk of raw PCM audio data to the streaming session. | ||
| * Can be called from any context. Chunks are internally queued | ||
| * and serialized to native core one at a time. | ||
| * | ||
| * @param pcmData - Raw PCM audio bytes matching the configured format. | ||
| */ | ||
| async append(pcmData) { | ||
| if (!this.started || this.stopped) { | ||
| throw new Error('No active streaming session. Call start() first.'); | ||
| } | ||
| const copy = new Uint8Array(pcmData.length); | ||
| copy.set(pcmData); | ||
| await this.pushQueue.write(copy); | ||
| } | ||
| /** | ||
| * Internal loop that drains the push queue and sends chunks to native core one at a time. | ||
| * Terminates the session on any native error. | ||
| * @internal | ||
| */ | ||
| async pushLoop() { | ||
| try { | ||
| for await (const audioData of this.pushQueue) { | ||
| if (this.sessionAbortController?.signal.aborted) { | ||
| break; | ||
| } | ||
| try { | ||
| const responseData = this.coreInterop.executeCommandWithBinary("audio_stream_push", { | ||
| Params: { | ||
| SessionHandle: this.sessionHandle, | ||
| } | ||
| }, audioData); | ||
| // Parse transcription result from push response and surface it | ||
| if (responseData) { | ||
| try { | ||
| const result = parseTranscriptionResult(responseData); | ||
| const text = result.content?.[0]?.text; | ||
| if (text !== undefined && text !== null && text !== '') { | ||
| this.outputQueue?.tryWrite(result); | ||
| } | ||
| } | ||
| catch { | ||
| // Non-fatal: log and continue if response isn't a transcription result | ||
| } | ||
| } | ||
| } | ||
| catch (error) { | ||
| const errorMsg = error instanceof Error ? error.message : String(error); | ||
| const errorInfo = tryParseCoreError(errorMsg); | ||
| const fatalError = new Error(`Push failed (code=${errorInfo?.code ?? 'UNKNOWN'}): ${errorMsg}`, { cause: error }); | ||
| this.stopped = true; | ||
| this.started = false; | ||
| this.pushQueue?.complete(fatalError); | ||
| this.outputQueue?.complete(fatalError); | ||
| return; | ||
| } | ||
| } | ||
| } | ||
| catch (error) { | ||
| if (this.sessionAbortController?.signal.aborted) { | ||
| return; | ||
| } | ||
| const err = error instanceof Error ? error : new Error(String(error)); | ||
| this.outputQueue?.complete(new Error('Push loop terminated unexpectedly.', { cause: err })); | ||
| } | ||
| } | ||
| /** | ||
| * Get the async iterable of transcription results. | ||
| * Results arrive as the native ASR engine processes audio data. | ||
| * | ||
| * Usage: | ||
| * ```ts | ||
| * for await (const result of session.getStream()) { | ||
| * console.log(result.content[0].text); | ||
| * } | ||
| * ``` | ||
| */ | ||
| async *getStream() { | ||
| if (!this.outputQueue) { | ||
| throw new Error('No active streaming session. Call start() first.'); | ||
| } | ||
| if (this.streamConsumed) { | ||
| throw new Error('getStream() can only be called once per session. The output stream has already been consumed.'); | ||
| } | ||
| this.streamConsumed = true; | ||
| for await (const item of this.outputQueue) { | ||
| yield item; | ||
| } | ||
| } | ||
| /** | ||
| * Signal end-of-audio and stop the streaming session. | ||
| * Any remaining buffered audio in the push queue will be drained to native core first. | ||
| * Final results are delivered through getStream() before it completes. | ||
| */ | ||
| async stop() { | ||
| if (!this.started || this.stopped) { | ||
| return; | ||
| } | ||
| this.stopped = true; | ||
| this.pushQueue?.complete(); | ||
| if (this.pushLoopPromise) { | ||
| await this.pushLoopPromise; | ||
| } | ||
| this.sessionAbortController?.abort(); | ||
| let stopError = null; | ||
| try { | ||
| const responseData = this.coreInterop.executeCommand("audio_stream_stop", { | ||
| Params: { SessionHandle: this.sessionHandle } | ||
| }); | ||
| // Parse final transcription from stop response | ||
| if (responseData) { | ||
| try { | ||
| const finalResult = parseTranscriptionResult(responseData); | ||
| if (finalResult.content?.[0]?.text) { | ||
| this.outputQueue?.tryWrite(finalResult); | ||
| } | ||
| } | ||
| catch { | ||
| // Non-fatal | ||
| } | ||
| } | ||
| } | ||
| catch (error) { | ||
| stopError = error instanceof Error ? error : new Error(String(error)); | ||
| } | ||
| this.sessionHandle = null; | ||
| this.started = false; | ||
| this.sessionAbortController = null; | ||
| this.outputQueue?.complete(); | ||
| if (stopError) { | ||
| throw new Error(`Error stopping audio stream session: ${stopError.message}`, { cause: stopError }); | ||
| } | ||
| } | ||
| /** | ||
| * Dispose the client and stop any active session. | ||
| * Safe to call multiple times. | ||
| */ | ||
| async dispose() { | ||
| try { | ||
| if (this.started && !this.stopped) { | ||
| await this.stop(); | ||
| } | ||
| } | ||
| catch { | ||
| // Swallow errors during best-effort cleanup to keep dispose() silent. | ||
| } | ||
| } | ||
| } | ||
| //# sourceMappingURL=liveAudioSession.js.map |
| /** | ||
| * Types for real-time audio streaming transcription results and structured errors. | ||
| * Mirrors the C# LiveAudioTranscriptionResponse (extends ConversationItem) and CoreErrorResponse. | ||
| */ | ||
| /** | ||
| * A content part within a transcription result. | ||
| * Follows the OpenAI Realtime API's ContentPart pattern. | ||
| */ | ||
| export interface TranscriptionContentPart { | ||
| /** The transcribed text. */ | ||
| text?: string | null; | ||
| /** Alias for text, matching the OpenAI Realtime API's ContentPart.transcript field. */ | ||
| transcript?: string | null; | ||
| } | ||
| /** | ||
| * A transcription result from a real-time audio streaming session. | ||
| * Shaped like the OpenAI Realtime API's ConversationItem so that | ||
| * customers access text via result.content[0].text or result.content[0].transcript. | ||
| */ | ||
| export interface LiveAudioTranscriptionResponse { | ||
| /** Unique identifier for this result (if available). */ | ||
| id?: string | null; | ||
| /** Whether this is a partial (interim) or final result for this segment. */ | ||
| is_final: boolean; | ||
| /** The transcription content parts. Access text via content[0].text or content[0].transcript. */ | ||
| content: TranscriptionContentPart[]; | ||
| /** Start time offset of this segment in the audio stream (seconds). */ | ||
| start_time?: number | null; | ||
| /** End time offset of this segment in the audio stream (seconds). */ | ||
| end_time?: number | null; | ||
| } | ||
| /** | ||
| * Parse raw Core JSON response into a LiveAudioTranscriptionResponse. | ||
| * Maps the flat Core format (text, is_final, start_time, end_time) into | ||
| * the ConversationItem-shaped result with content[0].text and content[0].transcript. | ||
| * @internal | ||
| */ | ||
| export declare function parseTranscriptionResult(json: string): LiveAudioTranscriptionResponse; | ||
| /** | ||
| * Structured error response from native core audio streaming commands. | ||
| */ | ||
| export interface CoreErrorResponse { | ||
| /** Machine-readable error code. */ | ||
| code: string; | ||
| /** Human-readable error message. */ | ||
| message: string; | ||
| /** Whether this error is transient and may succeed on retry. */ | ||
| isTransient: boolean; | ||
| } | ||
| /** | ||
| * Attempt to parse a native error string as a structured CoreErrorResponse. | ||
| * Handles both raw JSON and CoreInterop-prefixed messages | ||
| * (e.g., "Command 'X' failed: {...}"). | ||
| * Returns null if no valid CoreErrorResponse JSON is found. | ||
| * @internal | ||
| */ | ||
| export declare function tryParseCoreError(errorString: string): CoreErrorResponse | null; |
| /** | ||
| * Types for real-time audio streaming transcription results and structured errors. | ||
| * Mirrors the C# LiveAudioTranscriptionResponse (extends ConversationItem) and CoreErrorResponse. | ||
| */ | ||
| /** | ||
| * Parse raw Core JSON response into a LiveAudioTranscriptionResponse. | ||
| * Maps the flat Core format (text, is_final, start_time, end_time) into | ||
| * the ConversationItem-shaped result with content[0].text and content[0].transcript. | ||
| * @internal | ||
| */ | ||
| export function parseTranscriptionResult(json) { | ||
| const raw = JSON.parse(json); | ||
| return { | ||
| id: raw.id ?? null, | ||
| is_final: raw.is_final ?? false, | ||
| start_time: raw.start_time ?? null, | ||
| end_time: raw.end_time ?? null, | ||
| content: [ | ||
| { | ||
| text: raw.text ?? '', | ||
| transcript: raw.text ?? '' | ||
| } | ||
| ] | ||
| }; | ||
| } | ||
| /** | ||
| * Attempt to parse a native error string as a structured CoreErrorResponse. | ||
| * Handles both raw JSON and CoreInterop-prefixed messages | ||
| * (e.g., "Command 'X' failed: {...}"). | ||
| * Returns null if no valid CoreErrorResponse JSON is found. | ||
| * @internal | ||
| */ | ||
| export function tryParseCoreError(errorString) { | ||
| // Try raw JSON first, then extract JSON after "failed: " prefix | ||
| const candidates = [errorString]; | ||
| const prefixIdx = errorString.indexOf('failed: '); | ||
| if (prefixIdx !== -1) { | ||
| candidates.push(errorString.substring(prefixIdx + 8)); | ||
| } | ||
| for (const candidate of candidates) { | ||
| try { | ||
| const parsed = JSON.parse(candidate); | ||
| if (typeof parsed.code === 'string' && typeof parsed.message === 'string' && typeof parsed.isTransient === 'boolean') { | ||
| return parsed; | ||
| } | ||
| } | ||
| catch { | ||
| // not valid JSON, try next candidate | ||
| } | ||
| } | ||
| return null; | ||
| } | ||
| //# sourceMappingURL=liveAudioTypes.js.map |
| import { ResponseCreateParams, ResponseObject, ResponseToolChoice, TruncationStrategy, TextConfig, ReasoningConfig, StreamingEvent, InputItemsListResponse, DeleteResponseResult, ResponseInputItem } from '../types.js'; | ||
| /** | ||
| * Extracts the text content from an assistant message in a Response. | ||
| * Equivalent to OpenAI Python SDK's `response.output_text`. | ||
| * | ||
| * @param response - The Response object. | ||
| * @returns The concatenated text from the first assistant message, or an empty string. | ||
| */ | ||
| export declare function getOutputText(response: ResponseObject): string; | ||
| /** | ||
| * Configuration settings for the Responses API client. | ||
| * Properties use camelCase in JS and are serialized to snake_case for the API. | ||
| */ | ||
| export declare class ResponsesClientSettings { | ||
| /** System-level instructions to guide the model. */ | ||
| instructions?: string; | ||
| temperature?: number; | ||
| topP?: number; | ||
| maxOutputTokens?: number; | ||
| frequencyPenalty?: number; | ||
| presencePenalty?: number; | ||
| toolChoice?: ResponseToolChoice; | ||
| truncation?: TruncationStrategy; | ||
| parallelToolCalls?: boolean; | ||
| store?: boolean; | ||
| metadata?: Record<string, string>; | ||
| reasoning?: ReasoningConfig; | ||
| text?: TextConfig; | ||
| seed?: number; | ||
| /** | ||
| * Serializes settings into an OpenAI Responses API-compatible request object. | ||
| * @internal | ||
| */ | ||
| _serialize(): Partial<ResponseCreateParams>; | ||
| } | ||
| /** | ||
| * Client for the OpenAI Responses API served by Foundry Local's embedded web service. | ||
| * | ||
| * Unlike ChatClient/AudioClient (which use FFI via CoreInterop), the Responses API | ||
| * is HTTP-only. This client uses fetch() for all operations and parses Server-Sent Events | ||
| * for streaming. | ||
| * | ||
| * Create via `FoundryLocalManager.createResponsesClient()` or | ||
| * `model.createResponsesClient(baseUrl)`. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * const manager = FoundryLocalManager.create({ appName: 'MyApp' }); | ||
| * manager.startWebService(); | ||
| * const client = manager.createResponsesClient('my-model-id'); | ||
| * | ||
| * // Non-streaming | ||
| * const response = await client.create('Hello, world!'); | ||
| * console.log(response.output); | ||
| * | ||
| * // Streaming | ||
| * await client.createStreaming('Tell me a story', (event) => { | ||
| * if (event.type === 'response.output_text.delta') { | ||
| * process.stdout.write(event.delta); | ||
| * } | ||
| * }); | ||
| * ``` | ||
| */ | ||
| export declare class ResponsesClient { | ||
| private baseUrl; | ||
| private modelId?; | ||
| /** | ||
| * Configuration settings for responses. | ||
| */ | ||
| settings: ResponsesClientSettings; | ||
| /** | ||
| * @param baseUrl - The base URL of the Foundry Local web service (e.g. "http://127.0.0.1:5273"). | ||
| * @param modelId - Optional default model ID. Can be overridden per-request via options. | ||
| */ | ||
| constructor(baseUrl: string, modelId?: string); | ||
| /** | ||
| * Creates a model response (non-streaming). | ||
| * @param input - A string prompt or array of input items. | ||
| * @param options - Additional request parameters that override client settings. | ||
| * The `model` field is optional here if a default model was set in the constructor. | ||
| * @returns The completed Response object. Check `response.status` and `response.error` | ||
| * even on success — the server returns HTTP 200 for model-level failures too. | ||
| */ | ||
| create(input: string | ResponseInputItem[], options?: Partial<ResponseCreateParams>): Promise<ResponseObject>; | ||
| /** | ||
| * Creates a model response with streaming via Server-Sent Events. | ||
| * @param input - A string prompt or array of input items. | ||
| * @param callback - Called for each streaming event received. | ||
| * @param options - Additional request parameters that override client settings. | ||
| */ | ||
| createStreaming(input: string | ResponseInputItem[], callback: (event: StreamingEvent) => void, options?: Partial<ResponseCreateParams>): Promise<void>; | ||
| /** | ||
| * Retrieves a stored response by ID. | ||
| * @param responseId - The ID of the response to retrieve. | ||
| * @returns The Response object, or throws if not found. | ||
| */ | ||
| get(responseId: string): Promise<ResponseObject>; | ||
| /** | ||
| * Deletes a stored response by ID. | ||
| * @param responseId - The ID of the response to delete. | ||
| * @returns The deletion result. | ||
| */ | ||
| delete(responseId: string): Promise<DeleteResponseResult>; | ||
| /** | ||
| * Cancels an in-progress response. | ||
| * @param responseId - The ID of the response to cancel. | ||
| * @returns The cancelled Response object. | ||
| */ | ||
| cancel(responseId: string): Promise<ResponseObject>; | ||
| /** | ||
| * Retrieves input items for a stored response. | ||
| * @param responseId - The ID of the response. | ||
| * @returns The list of input items. | ||
| */ | ||
| getInputItems(responseId: string): Promise<InputItemsListResponse>; | ||
| /** | ||
| * Builds the full request body by merging input, settings, and per-call options. | ||
| */ | ||
| private buildRequest; | ||
| /** | ||
| * Validates that input is a non-empty string or a non-empty array of items. | ||
| */ | ||
| private validateInput; | ||
| /** | ||
| * Validates that tools array is properly formed. | ||
| * Follows the same pattern as ChatClient.validateTools. | ||
| */ | ||
| private validateTools; | ||
| /** | ||
| * Validates that a string ID parameter is non-empty and within length bounds. | ||
| */ | ||
| private validateId; | ||
| /** | ||
| * Performs a fetch and parses the JSON response, handling errors. | ||
| */ | ||
| private fetchJson; | ||
| /** | ||
| * Low-level fetch wrapper with error handling. | ||
| */ | ||
| private doFetch; | ||
| /** | ||
| * Parses a Server-Sent Events stream from the fetch response body. | ||
| * Format: "event: {type}\ndata: {json}\n\n" | ||
| * Terminal signal: "data: [DONE]\n\n" | ||
| * Per SSE spec, multiple data: lines within a single event are joined with \n. | ||
| */ | ||
| private parseSSEStream; | ||
| } |
| /** | ||
| * Extracts the text content from an assistant message in a Response. | ||
| * Equivalent to OpenAI Python SDK's `response.output_text`. | ||
| * | ||
| * @param response - The Response object. | ||
| * @returns The concatenated text from the first assistant message, or an empty string. | ||
| */ | ||
| export function getOutputText(response) { | ||
| for (const item of response.output) { | ||
| if (item.type === 'message' && item.role === 'assistant') { | ||
| const content = item.content; | ||
| if (typeof content === 'string') | ||
| return content; | ||
| if (Array.isArray(content)) { | ||
| return content | ||
| .filter((p) => 'text' in p) | ||
| .map((p) => p.text) | ||
| .join(''); | ||
| } | ||
| } | ||
| } | ||
| return ''; | ||
| } | ||
| /** | ||
| * Configuration settings for the Responses API client. | ||
| * Properties use camelCase in JS and are serialized to snake_case for the API. | ||
| */ | ||
| export class ResponsesClientSettings { | ||
| /** System-level instructions to guide the model. */ | ||
| instructions; | ||
| temperature; | ||
| topP; | ||
| maxOutputTokens; | ||
| frequencyPenalty; | ||
| presencePenalty; | ||
| toolChoice; | ||
| truncation; | ||
| parallelToolCalls; | ||
| store; | ||
| metadata; | ||
| reasoning; | ||
| text; | ||
| seed; | ||
| /** | ||
| * Serializes settings into an OpenAI Responses API-compatible request object. | ||
| * @internal | ||
| */ | ||
| _serialize() { | ||
| const filterUndefined = (obj) => Object.fromEntries(Object.entries(obj).filter(([_, v]) => v !== undefined)); | ||
| const result = { | ||
| instructions: this.instructions, | ||
| temperature: this.temperature, | ||
| top_p: this.topP, | ||
| max_output_tokens: this.maxOutputTokens, | ||
| frequency_penalty: this.frequencyPenalty, | ||
| presence_penalty: this.presencePenalty, | ||
| tool_choice: this.toolChoice, | ||
| truncation: this.truncation, | ||
| parallel_tool_calls: this.parallelToolCalls, | ||
| store: this.store, | ||
| metadata: this.metadata, | ||
| reasoning: this.reasoning ? filterUndefined(this.reasoning) : undefined, | ||
| text: this.text ? filterUndefined(this.text) : undefined, | ||
| seed: this.seed, | ||
| }; | ||
| // Filter out undefined properties | ||
| return filterUndefined(result); | ||
| } | ||
| } | ||
| /** | ||
| * Client for the OpenAI Responses API served by Foundry Local's embedded web service. | ||
| * | ||
| * Unlike ChatClient/AudioClient (which use FFI via CoreInterop), the Responses API | ||
| * is HTTP-only. This client uses fetch() for all operations and parses Server-Sent Events | ||
| * for streaming. | ||
| * | ||
| * Create via `FoundryLocalManager.createResponsesClient()` or | ||
| * `model.createResponsesClient(baseUrl)`. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * const manager = FoundryLocalManager.create({ appName: 'MyApp' }); | ||
| * manager.startWebService(); | ||
| * const client = manager.createResponsesClient('my-model-id'); | ||
| * | ||
| * // Non-streaming | ||
| * const response = await client.create('Hello, world!'); | ||
| * console.log(response.output); | ||
| * | ||
| * // Streaming | ||
| * await client.createStreaming('Tell me a story', (event) => { | ||
| * if (event.type === 'response.output_text.delta') { | ||
| * process.stdout.write(event.delta); | ||
| * } | ||
| * }); | ||
| * ``` | ||
| */ | ||
| export class ResponsesClient { | ||
| baseUrl; | ||
| modelId; | ||
| /** | ||
| * Configuration settings for responses. | ||
| */ | ||
| settings = new ResponsesClientSettings(); | ||
| /** | ||
| * @param baseUrl - The base URL of the Foundry Local web service (e.g. "http://127.0.0.1:5273"). | ||
| * @param modelId - Optional default model ID. Can be overridden per-request via options. | ||
| */ | ||
| constructor(baseUrl, modelId) { | ||
| if (!baseUrl || typeof baseUrl !== 'string' || baseUrl.trim() === '') { | ||
| throw new Error('baseUrl must be a non-empty string.'); | ||
| } | ||
| // Strip trailing slashes for consistent URL construction | ||
| let url = baseUrl; | ||
| while (url.endsWith('/')) { | ||
| url = url.slice(0, -1); | ||
| } | ||
| this.baseUrl = url; | ||
| this.modelId = modelId; | ||
| } | ||
| // ======================================================================== | ||
| // Public API | ||
| // ======================================================================== | ||
| /** | ||
| * Creates a model response (non-streaming). | ||
| * @param input - A string prompt or array of input items. | ||
| * @param options - Additional request parameters that override client settings. | ||
| * The `model` field is optional here if a default model was set in the constructor. | ||
| * @returns The completed Response object. Check `response.status` and `response.error` | ||
| * even on success — the server returns HTTP 200 for model-level failures too. | ||
| */ | ||
| async create(input, options) { | ||
| this.validateInput(input); | ||
| if (options?.tools) { | ||
| this.validateTools(options.tools); | ||
| } | ||
| const body = this.buildRequest(input, { ...options, stream: false }); | ||
| const response = await this.fetchJson('/v1/responses', { method: 'POST', body: JSON.stringify(body) }); | ||
| return response; | ||
| } | ||
| /** | ||
| * Creates a model response with streaming via Server-Sent Events. | ||
| * @param input - A string prompt or array of input items. | ||
| * @param callback - Called for each streaming event received. | ||
| * @param options - Additional request parameters that override client settings. | ||
| */ | ||
| async createStreaming(input, callback, options) { | ||
| this.validateInput(input); | ||
| if (options?.tools) { | ||
| this.validateTools(options.tools); | ||
| } | ||
| if (!callback || typeof callback !== 'function') { | ||
| throw new Error('Callback must be a valid function.'); | ||
| } | ||
| const body = this.buildRequest(input, { ...options, stream: true }); | ||
| const res = await this.doFetch('/v1/responses', { | ||
| method: 'POST', | ||
| headers: { 'Content-Type': 'application/json', 'Accept': 'text/event-stream' }, | ||
| body: JSON.stringify(body), | ||
| }); | ||
| if (!res.body) { | ||
| throw new Error('Streaming response has no body.'); | ||
| } | ||
| let error = null; | ||
| await this.parseSSEStream(res.body, (event) => { | ||
| if (error) | ||
| return; | ||
| try { | ||
| callback(event); | ||
| } | ||
| catch (e) { | ||
| error = new Error(`User callback threw an error: ${e instanceof Error ? e.message : String(e)}`, { cause: e }); | ||
| } | ||
| }); | ||
| if (error) { | ||
| throw error; | ||
| } | ||
| } | ||
| /** | ||
| * Retrieves a stored response by ID. | ||
| * @param responseId - The ID of the response to retrieve. | ||
| * @returns The Response object, or throws if not found. | ||
| */ | ||
| async get(responseId) { | ||
| this.validateId(responseId, 'responseId'); | ||
| return this.fetchJson(`/v1/responses/${encodeURIComponent(responseId)}`, { method: 'GET' }); | ||
| } | ||
| /** | ||
| * Deletes a stored response by ID. | ||
| * @param responseId - The ID of the response to delete. | ||
| * @returns The deletion result. | ||
| */ | ||
| async delete(responseId) { | ||
| this.validateId(responseId, 'responseId'); | ||
| return this.fetchJson(`/v1/responses/${encodeURIComponent(responseId)}`, { method: 'DELETE' }); | ||
| } | ||
| /** | ||
| * Cancels an in-progress response. | ||
| * @param responseId - The ID of the response to cancel. | ||
| * @returns The cancelled Response object. | ||
| */ | ||
| async cancel(responseId) { | ||
| this.validateId(responseId, 'responseId'); | ||
| return this.fetchJson(`/v1/responses/${encodeURIComponent(responseId)}/cancel`, { method: 'POST' }); | ||
| } | ||
| /** | ||
| * Retrieves input items for a stored response. | ||
| * @param responseId - The ID of the response. | ||
| * @returns The list of input items. | ||
| */ | ||
| async getInputItems(responseId) { | ||
| this.validateId(responseId, 'responseId'); | ||
| return this.fetchJson(`/v1/responses/${encodeURIComponent(responseId)}/input_items`, { method: 'GET' }); | ||
| } | ||
| // ======================================================================== | ||
| // Internal helpers | ||
| // ======================================================================== | ||
| /** | ||
| * Builds the full request body by merging input, settings, and per-call options. | ||
| */ | ||
| buildRequest(input, options) { | ||
| const model = options?.model ?? this.modelId; | ||
| if (!model || typeof model !== 'string' || model.trim() === '') { | ||
| throw new Error('Model must be specified either in the constructor, via createResponsesClient(modelId), or in options.model.'); | ||
| } | ||
| const serializedSettings = this.settings._serialize(); | ||
| // Merge order: model+input → settings defaults → per-call overrides | ||
| return { | ||
| model, | ||
| input, | ||
| ...serializedSettings, | ||
| ...options, | ||
| }; | ||
| } | ||
| /** | ||
| * Validates that input is a non-empty string or a non-empty array of items. | ||
| */ | ||
| validateInput(input) { | ||
| if (input === null || input === undefined) { | ||
| throw new Error('Input cannot be null or undefined.'); | ||
| } | ||
| if (typeof input === 'string') { | ||
| if (input.trim() === '') { | ||
| throw new Error('Input string cannot be empty.'); | ||
| } | ||
| return; | ||
| } | ||
| if (Array.isArray(input)) { | ||
| if (input.length === 0) { | ||
| throw new Error('Input items array cannot be empty.'); | ||
| } | ||
| for (const item of input) { | ||
| if (!item || typeof item !== 'object') { | ||
| throw new Error('Each input item must be a non-null object.'); | ||
| } | ||
| if (typeof item.type !== 'string' || item.type.trim() === '') { | ||
| throw new Error('Each input item must have a "type" property that is a non-empty string.'); | ||
| } | ||
| } | ||
| return; | ||
| } | ||
| throw new Error('Input must be a string or an array of input items.'); | ||
| } | ||
| /** | ||
| * Validates that tools array is properly formed. | ||
| * Follows the same pattern as ChatClient.validateTools. | ||
| */ | ||
| validateTools(tools) { | ||
| if (!Array.isArray(tools)) { | ||
| throw new Error('Tools must be an array if provided.'); | ||
| } | ||
| for (const tool of tools) { | ||
| if (!tool || typeof tool !== 'object' || Array.isArray(tool)) { | ||
| throw new Error('Each tool must be a non-null object with a valid "type" and "name".'); | ||
| } | ||
| if (tool.type !== 'function') { | ||
| throw new Error('Each tool must have type "function".'); | ||
| } | ||
| if (typeof tool.name !== 'string' || tool.name.trim() === '') { | ||
| throw new Error('Each tool must have a "name" property that is a non-empty string.'); | ||
| } | ||
| } | ||
| } | ||
| /** | ||
| * Validates that a string ID parameter is non-empty and within length bounds. | ||
| */ | ||
| validateId(id, paramName) { | ||
| if (!id || typeof id !== 'string' || id.trim() === '') { | ||
| throw new Error(`${paramName} must be a non-empty string.`); | ||
| } | ||
| if (id.length > 1024) { | ||
| throw new Error(`${paramName} exceeds maximum length (1024).`); | ||
| } | ||
| } | ||
| /** | ||
| * Performs a fetch and parses the JSON response, handling errors. | ||
| */ | ||
| async fetchJson(path, init) { | ||
| const res = await this.doFetch(path, { | ||
| ...init, | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| ...(init.headers || {}), | ||
| }, | ||
| }); | ||
| const text = await res.text(); | ||
| try { | ||
| return JSON.parse(text); | ||
| } | ||
| catch { | ||
| throw new Error(`Failed to parse response JSON: ${text.substring(0, 200)}`); | ||
| } | ||
| } | ||
| /** | ||
| * Low-level fetch wrapper with error handling. | ||
| */ | ||
| async doFetch(path, init) { | ||
| const url = `${this.baseUrl}${path}`; | ||
| let res; | ||
| try { | ||
| res = await fetch(url, init); | ||
| } | ||
| catch (e) { | ||
| throw new Error(`Network error calling ${init.method ?? 'GET'} ${path}: ${e instanceof Error ? e.message : String(e)}`, { cause: e }); | ||
| } | ||
| if (!res.ok) { | ||
| const errorText = await res.text().catch(() => res.statusText); | ||
| throw new Error(`Responses API error (${res.status}): ${errorText}`); | ||
| } | ||
| return res; | ||
| } | ||
| /** | ||
| * Parses a Server-Sent Events stream from the fetch response body. | ||
| * Format: "event: {type}\ndata: {json}\n\n" | ||
| * Terminal signal: "data: [DONE]\n\n" | ||
| * Per SSE spec, multiple data: lines within a single event are joined with \n. | ||
| */ | ||
| async parseSSEStream(body, onEvent) { | ||
| const reader = body.getReader(); | ||
| const decoder = new TextDecoder(); | ||
| const bufferParts = []; | ||
| let parseError = null; | ||
| try { | ||
| while (true) { | ||
| const { done, value } = await reader.read(); | ||
| if (done) | ||
| break; | ||
| bufferParts.push(decoder.decode(value, { stream: true })); | ||
| const buffer = bufferParts.join(''); | ||
| // Process complete SSE blocks (separated by double newlines) | ||
| const blocks = buffer.split('\n\n'); | ||
| // Keep the last (potentially incomplete) block for next iteration | ||
| const incomplete = blocks.pop() ?? ''; | ||
| bufferParts.length = 0; | ||
| if (incomplete) | ||
| bufferParts.push(incomplete); | ||
| for (const block of blocks) { | ||
| if (parseError) | ||
| break; | ||
| const trimmed = block.trim(); | ||
| if (!trimmed) | ||
| continue; | ||
| // Check for terminal signal | ||
| if (trimmed === 'data: [DONE]') { | ||
| return; | ||
| } | ||
| // Parse SSE fields — per spec, multiple data: lines are joined with \n | ||
| const dataLines = []; | ||
| for (const line of trimmed.split('\n')) { | ||
| if (line.startsWith('data: ')) { | ||
| dataLines.push(line.slice(6)); | ||
| } | ||
| else if (line === 'data:') { | ||
| dataLines.push(''); | ||
| } | ||
| // 'event:' field is informational; the type is inside the JSON data | ||
| } | ||
| const eventData = dataLines.length > 0 ? dataLines.join('\n') : undefined; | ||
| if (eventData) { | ||
| try { | ||
| const parsed = JSON.parse(eventData); | ||
| onEvent(parsed); | ||
| } | ||
| catch (e) { | ||
| parseError = new Error(`Failed to parse streaming event: ${e instanceof Error ? e.message : String(e)}`, { cause: e }); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| finally { | ||
| reader.releaseLock(); | ||
| } | ||
| if (parseError) { | ||
| throw parseError; | ||
| } | ||
| } | ||
| } | ||
| //# sourceMappingURL=responsesClient.js.map |
| export declare enum DeviceType { | ||
| Invalid = "Invalid", | ||
| CPU = "CPU", | ||
| GPU = "GPU", | ||
| NPU = "NPU" | ||
| } | ||
| export interface PromptTemplate { | ||
| system?: string | null; | ||
| user?: string | null; | ||
| assistant: string; | ||
| prompt: string; | ||
| } | ||
| export interface Runtime { | ||
| deviceType: DeviceType; | ||
| executionProvider: string; | ||
| } | ||
| export interface Parameter { | ||
| name: string; | ||
| value?: string | null; | ||
| } | ||
| export interface ModelSettings { | ||
| parameters?: Parameter[] | null; | ||
| } | ||
| export interface ModelInfo { | ||
| id: string; | ||
| name: string; | ||
| version: number; | ||
| alias: string; | ||
| displayName?: string | null; | ||
| providerType: string; | ||
| uri: string; | ||
| modelType: string; | ||
| promptTemplate?: PromptTemplate | null; | ||
| publisher?: string | null; | ||
| modelSettings?: ModelSettings | null; | ||
| license?: string | null; | ||
| licenseDescription?: string | null; | ||
| cached: boolean; | ||
| task?: string | null; | ||
| runtime?: Runtime | null; | ||
| fileSizeMb?: number | null; | ||
| supportsToolCalling?: boolean | null; | ||
| maxOutputTokens?: number | null; | ||
| minFLVersion?: string | null; | ||
| createdAtUnix: number; | ||
| contextLength?: number | null; | ||
| inputModalities?: string | null; | ||
| outputModalities?: string | null; | ||
| capabilities?: string | null; | ||
| } | ||
| export interface ResponseFormat { | ||
| type: string; | ||
| jsonSchema?: string; | ||
| larkGrammar?: string; | ||
| } | ||
| export interface ToolChoice { | ||
| type: string; | ||
| name?: string; | ||
| } | ||
| /** Describes a discoverable execution provider. */ | ||
| export interface EpInfo { | ||
| /** The identifier of the execution provider (e.g. "CUDAExecutionProvider"). */ | ||
| name: string; | ||
| /** True if this EP has already been successfully downloaded and registered. */ | ||
| isRegistered: boolean; | ||
| } | ||
| /** Result of an explicit EP download and registration operation. */ | ||
| export interface EpDownloadResult { | ||
| /** True if all requested EPs were successfully downloaded and registered. */ | ||
| success: boolean; | ||
| /** Human-readable status message. */ | ||
| status: string; | ||
| /** Names of EPs that were successfully registered. */ | ||
| registeredEps: string[]; | ||
| /** Names of EPs that failed to register. */ | ||
| failedEps: string[]; | ||
| } | ||
| /** Status of a Response object. */ | ||
| export type ResponseStatus = 'queued' | 'in_progress' | 'completed' | 'failed' | 'incomplete' | 'cancelled'; | ||
| /** Role of a message in the Responses API. */ | ||
| export type MessageRole = 'system' | 'user' | 'assistant' | 'developer'; | ||
| /** Status of an individual response item. */ | ||
| export type ResponseItemStatus = 'in_progress' | 'completed' | 'incomplete'; | ||
| /** Controls which tool the model should use. */ | ||
| export type ResponseToolChoice = 'none' | 'auto' | 'required' | ResponseToolChoiceFunction; | ||
| export interface ResponseToolChoiceFunction { | ||
| type: 'function'; | ||
| name: string; | ||
| } | ||
| /** Truncation strategy. */ | ||
| export type TruncationStrategy = 'auto' | 'disabled'; | ||
| /** Service tier. */ | ||
| export type ServiceTier = 'default' | 'auto' | 'flex' | 'priority'; | ||
| export interface InputTextContent { | ||
| type: 'input_text'; | ||
| text: string; | ||
| } | ||
| export interface OutputTextContent { | ||
| type: 'output_text'; | ||
| text: string; | ||
| annotations?: Annotation[]; | ||
| logprobs?: LogProb[]; | ||
| } | ||
| export interface RefusalContent { | ||
| type: 'refusal'; | ||
| refusal: string; | ||
| } | ||
| export type ContentPart = InputTextContent | OutputTextContent | RefusalContent; | ||
| export interface Annotation { | ||
| type: string; | ||
| start_index: number; | ||
| end_index: number; | ||
| } | ||
| export interface UrlCitationAnnotation extends Annotation { | ||
| type: 'url_citation'; | ||
| url: string; | ||
| title: string; | ||
| } | ||
| export interface LogProb { | ||
| token: string; | ||
| logprob: number; | ||
| bytes?: number[]; | ||
| } | ||
| export interface FunctionToolDefinition { | ||
| type: 'function'; | ||
| name: string; | ||
| description?: string; | ||
| parameters?: Record<string, unknown>; | ||
| strict?: boolean; | ||
| } | ||
| export interface MessageItem { | ||
| type: 'message'; | ||
| id?: string; | ||
| role: MessageRole; | ||
| content: string | ContentPart[]; | ||
| status?: ResponseItemStatus; | ||
| } | ||
| export interface FunctionCallItem { | ||
| type: 'function_call'; | ||
| id?: string; | ||
| call_id: string; | ||
| name: string; | ||
| arguments: string; | ||
| status?: ResponseItemStatus; | ||
| } | ||
| export interface FunctionCallOutputItem { | ||
| type: 'function_call_output'; | ||
| id?: string; | ||
| call_id: string; | ||
| output: string | ContentPart[]; | ||
| status?: ResponseItemStatus; | ||
| } | ||
| export interface ItemReference { | ||
| type: 'item_reference'; | ||
| id: string; | ||
| } | ||
| export interface ReasoningItem { | ||
| type: 'reasoning'; | ||
| id?: string; | ||
| content?: ContentPart[]; | ||
| encrypted_content?: string; | ||
| summary?: string; | ||
| status?: ResponseItemStatus; | ||
| } | ||
| export type ResponseInputItem = MessageItem | FunctionCallItem | FunctionCallOutputItem | ItemReference | ReasoningItem; | ||
| export type ResponseOutputItem = MessageItem | FunctionCallItem | ReasoningItem; | ||
| export interface ReasoningConfig { | ||
| effort?: string; | ||
| summary?: string; | ||
| } | ||
| export interface TextFormat { | ||
| type: string; | ||
| name?: string; | ||
| description?: string; | ||
| schema?: unknown; | ||
| strict?: boolean; | ||
| } | ||
| export interface TextConfig { | ||
| format?: TextFormat; | ||
| verbosity?: string; | ||
| } | ||
| export interface ResponseUsage { | ||
| input_tokens: number; | ||
| output_tokens: number; | ||
| total_tokens: number; | ||
| input_tokens_details?: { | ||
| cached_tokens: number; | ||
| }; | ||
| output_tokens_details?: { | ||
| reasoning_tokens: number; | ||
| }; | ||
| } | ||
| export interface ResponseError { | ||
| code: string; | ||
| message: string; | ||
| } | ||
| export interface IncompleteDetails { | ||
| reason: string; | ||
| } | ||
| export interface ResponseCreateParams { | ||
| model?: string; | ||
| input?: string | ResponseInputItem[]; | ||
| instructions?: string; | ||
| previous_response_id?: string; | ||
| tools?: FunctionToolDefinition[]; | ||
| tool_choice?: ResponseToolChoice; | ||
| temperature?: number; | ||
| top_p?: number; | ||
| max_output_tokens?: number; | ||
| frequency_penalty?: number; | ||
| presence_penalty?: number; | ||
| truncation?: TruncationStrategy; | ||
| parallel_tool_calls?: boolean; | ||
| store?: boolean; | ||
| metadata?: Record<string, string>; | ||
| stream?: boolean; | ||
| reasoning?: ReasoningConfig; | ||
| text?: TextConfig; | ||
| seed?: number; | ||
| user?: string; | ||
| } | ||
| export interface ResponseObject { | ||
| id: string; | ||
| object: 'response'; | ||
| created_at: number; | ||
| completed_at?: number | null; | ||
| failed_at?: number | null; | ||
| cancelled_at?: number | null; | ||
| status: ResponseStatus; | ||
| incomplete_details?: IncompleteDetails | null; | ||
| model: string; | ||
| previous_response_id?: string | null; | ||
| instructions?: string | null; | ||
| output: ResponseOutputItem[]; | ||
| error?: ResponseError | null; | ||
| tools: FunctionToolDefinition[]; | ||
| tool_choice: ResponseToolChoice; | ||
| truncation: TruncationStrategy; | ||
| parallel_tool_calls: boolean; | ||
| text: TextConfig; | ||
| top_p: number; | ||
| temperature: number; | ||
| presence_penalty: number; | ||
| frequency_penalty: number; | ||
| max_output_tokens?: number | null; | ||
| reasoning?: ReasoningConfig | null; | ||
| store: boolean; | ||
| metadata?: Record<string, string> | null; | ||
| usage?: ResponseUsage | null; | ||
| user?: string | null; | ||
| } | ||
| export interface InputItemsListResponse { | ||
| object: 'list'; | ||
| data: ResponseInputItem[]; | ||
| } | ||
| export interface DeleteResponseResult { | ||
| id: string; | ||
| object: string; | ||
| deleted: boolean; | ||
| } | ||
| export interface ResponseLifecycleEvent { | ||
| type: 'response.created' | 'response.queued' | 'response.in_progress' | 'response.completed' | 'response.failed' | 'response.incomplete'; | ||
| response: ResponseObject; | ||
| sequence_number: number; | ||
| } | ||
| export interface OutputItemAddedEvent { | ||
| type: 'response.output_item.added'; | ||
| item_id: string; | ||
| output_index: number; | ||
| item: ResponseOutputItem; | ||
| sequence_number: number; | ||
| } | ||
| export interface OutputItemDoneEvent { | ||
| type: 'response.output_item.done'; | ||
| item_id: string; | ||
| output_index: number; | ||
| item: ResponseOutputItem; | ||
| sequence_number: number; | ||
| } | ||
| export interface ContentPartAddedEvent { | ||
| type: 'response.content_part.added'; | ||
| item_id: string; | ||
| content_index: number; | ||
| part: ContentPart; | ||
| sequence_number: number; | ||
| } | ||
| export interface ContentPartDoneEvent { | ||
| type: 'response.content_part.done'; | ||
| item_id: string; | ||
| content_index: number; | ||
| part: ContentPart; | ||
| sequence_number: number; | ||
| } | ||
| export interface OutputTextDeltaEvent { | ||
| type: 'response.output_text.delta'; | ||
| item_id: string; | ||
| output_index: number; | ||
| content_index: number; | ||
| delta: string; | ||
| sequence_number: number; | ||
| } | ||
| export interface OutputTextDoneEvent { | ||
| type: 'response.output_text.done'; | ||
| item_id: string; | ||
| output_index: number; | ||
| content_index: number; | ||
| text: string; | ||
| sequence_number: number; | ||
| } | ||
| export interface RefusalDeltaEvent { | ||
| type: 'response.refusal.delta'; | ||
| item_id: string; | ||
| content_index: number; | ||
| delta: string; | ||
| sequence_number: number; | ||
| } | ||
| export interface RefusalDoneEvent { | ||
| type: 'response.refusal.done'; | ||
| item_id: string; | ||
| content_index: number; | ||
| refusal: string; | ||
| sequence_number: number; | ||
| } | ||
| export interface FunctionCallArgsDeltaEvent { | ||
| type: 'response.function_call_arguments.delta'; | ||
| item_id: string; | ||
| output_index: number; | ||
| delta: string; | ||
| sequence_number: number; | ||
| } | ||
| export interface FunctionCallArgsDoneEvent { | ||
| type: 'response.function_call_arguments.done'; | ||
| item_id: string; | ||
| output_index: number; | ||
| arguments: string; | ||
| name: string; | ||
| sequence_number: number; | ||
| } | ||
| export interface StreamingErrorEvent { | ||
| type: 'error'; | ||
| code?: string; | ||
| message?: string; | ||
| param?: string; | ||
| sequence_number: number; | ||
| } | ||
| export type StreamingEvent = ResponseLifecycleEvent | OutputItemAddedEvent | OutputItemDoneEvent | ContentPartAddedEvent | ContentPartDoneEvent | OutputTextDeltaEvent | OutputTextDoneEvent | RefusalDeltaEvent | RefusalDoneEvent | FunctionCallArgsDeltaEvent | FunctionCallArgsDoneEvent | StreamingErrorEvent; |
| // adapted from sdk\cs\src\FoundryModelInfo.cs | ||
| export var DeviceType; | ||
| (function (DeviceType) { | ||
| DeviceType["Invalid"] = "Invalid"; | ||
| DeviceType["CPU"] = "CPU"; | ||
| DeviceType["GPU"] = "GPU"; | ||
| DeviceType["NPU"] = "NPU"; | ||
| })(DeviceType || (DeviceType = {})); | ||
| //# sourceMappingURL=types.js.map |
| const Utils = require("./util"); | ||
| const pth = require("path"); | ||
| const ZipEntry = require("./zipEntry"); | ||
| const ZipFile = require("./zipFile"); | ||
| const get_Bool = (...val) => Utils.findLast(val, (c) => typeof c === "boolean"); | ||
| const get_Str = (...val) => Utils.findLast(val, (c) => typeof c === "string"); | ||
| const get_Fun = (...val) => Utils.findLast(val, (c) => typeof c === "function"); | ||
| const defaultOptions = { | ||
| // option "noSort" : if true it disables files sorting | ||
| noSort: false, | ||
| // read entries during load (initial loading may be slower) | ||
| readEntries: false, | ||
| // default method is none | ||
| method: Utils.Constants.NONE, | ||
| // file system | ||
| fs: null | ||
| }; | ||
| module.exports = function (/**String*/ input, /** object */ options) { | ||
| let inBuffer = null; | ||
| // create object based default options, allowing them to be overwritten | ||
| const opts = Object.assign(Object.create(null), defaultOptions); | ||
| // test input variable | ||
| if (input && "object" === typeof input) { | ||
| // if value is not buffer we accept it to be object with options | ||
| if (!(input instanceof Uint8Array)) { | ||
| Object.assign(opts, input); | ||
| input = opts.input ? opts.input : undefined; | ||
| if (opts.input) delete opts.input; | ||
| } | ||
| // if input is buffer | ||
| if (Buffer.isBuffer(input)) { | ||
| inBuffer = input; | ||
| opts.method = Utils.Constants.BUFFER; | ||
| input = undefined; | ||
| } | ||
| } | ||
| // assign options | ||
| Object.assign(opts, options); | ||
| // instanciate utils filesystem | ||
| const filetools = new Utils(opts); | ||
| if (typeof opts.decoder !== "object" || typeof opts.decoder.encode !== "function" || typeof opts.decoder.decode !== "function") { | ||
| opts.decoder = Utils.decoder; | ||
| } | ||
| // if input is file name we retrieve its content | ||
| if (input && "string" === typeof input) { | ||
| // load zip file | ||
| if (filetools.fs.existsSync(input)) { | ||
| opts.method = Utils.Constants.FILE; | ||
| opts.filename = input; | ||
| inBuffer = filetools.fs.readFileSync(input); | ||
| } else { | ||
| throw Utils.Errors.INVALID_FILENAME(); | ||
| } | ||
| } | ||
| // create variable | ||
| const _zip = new ZipFile(inBuffer, opts); | ||
| const { canonical, sanitize, zipnamefix } = Utils; | ||
| function getEntry(/**Object*/ entry) { | ||
| if (entry && _zip) { | ||
| var item; | ||
| // If entry was given as a file name | ||
| if (typeof entry === "string") item = _zip.getEntry(pth.posix.normalize(entry)); | ||
| // if entry was given as a ZipEntry object | ||
| if (typeof entry === "object" && typeof entry.entryName !== "undefined" && typeof entry.header !== "undefined") item = _zip.getEntry(entry.entryName); | ||
| if (item) { | ||
| return item; | ||
| } | ||
| } | ||
| return null; | ||
| } | ||
| function fixPath(zipPath) { | ||
| const { join, normalize, sep } = pth.posix; | ||
| // convert windows file separators and normalize | ||
| return join(pth.isAbsolute(zipPath) ? "/": '.', normalize(sep + zipPath.split("\\").join(sep) + sep)); | ||
| } | ||
| function filenameFilter(filterfn) { | ||
| if (filterfn instanceof RegExp) { | ||
| // if filter is RegExp wrap it | ||
| return (function (rx) { | ||
| return function (filename) { | ||
| return rx.test(filename); | ||
| }; | ||
| })(filterfn); | ||
| } else if ("function" !== typeof filterfn) { | ||
| // if filter is not function we will replace it | ||
| return () => true; | ||
| } | ||
| return filterfn; | ||
| } | ||
| // keep last character on folders | ||
| const relativePath = (local, entry) => { | ||
| let lastChar = entry.slice(-1); | ||
| lastChar = lastChar === filetools.sep ? filetools.sep : ""; | ||
| return pth.relative(local, entry) + lastChar; | ||
| }; | ||
| return { | ||
| /** | ||
| * Extracts the given entry from the archive and returns the content as a Buffer object | ||
| * @param {ZipEntry|string} entry ZipEntry object or String with the full path of the entry | ||
| * @param {Buffer|string} [pass] - password | ||
| * @return Buffer or Null in case of error | ||
| */ | ||
| readFile: function (entry, pass) { | ||
| var item = getEntry(entry); | ||
| return (item && item.getData(pass)) || null; | ||
| }, | ||
| /** | ||
| * Returns how many child elements has on entry (directories) on files it is always 0 | ||
| * @param {ZipEntry|string} entry ZipEntry object or String with the full path of the entry | ||
| * @returns {integer} | ||
| */ | ||
| childCount: function (entry) { | ||
| const item = getEntry(entry); | ||
| if (item) { | ||
| return _zip.getChildCount(item); | ||
| } | ||
| }, | ||
| /** | ||
| * Asynchronous readFile | ||
| * @param {ZipEntry|string} entry ZipEntry object or String with the full path of the entry | ||
| * @param {callback} callback | ||
| * | ||
| * @return Buffer or Null in case of error | ||
| */ | ||
| readFileAsync: function (entry, callback) { | ||
| var item = getEntry(entry); | ||
| if (item) { | ||
| item.getDataAsync(callback); | ||
| } else { | ||
| callback(null, "getEntry failed for:" + entry); | ||
| } | ||
| }, | ||
| /** | ||
| * Extracts the given entry from the archive and returns the content as plain text in the given encoding | ||
| * @param {ZipEntry|string} entry - ZipEntry object or String with the full path of the entry | ||
| * @param {string} encoding - Optional. If no encoding is specified utf8 is used | ||
| * | ||
| * @return String | ||
| */ | ||
| readAsText: function (entry, encoding) { | ||
| var item = getEntry(entry); | ||
| if (item) { | ||
| var data = item.getData(); | ||
| if (data && data.length) { | ||
| return data.toString(encoding || "utf8"); | ||
| } | ||
| } | ||
| return ""; | ||
| }, | ||
| /** | ||
| * Asynchronous readAsText | ||
| * @param {ZipEntry|string} entry ZipEntry object or String with the full path of the entry | ||
| * @param {callback} callback | ||
| * @param {string} [encoding] - Optional. If no encoding is specified utf8 is used | ||
| * | ||
| * @return String | ||
| */ | ||
| readAsTextAsync: function (entry, callback, encoding) { | ||
| var item = getEntry(entry); | ||
| if (item) { | ||
| item.getDataAsync(function (data, err) { | ||
| if (err) { | ||
| callback(data, err); | ||
| return; | ||
| } | ||
| if (data && data.length) { | ||
| callback(data.toString(encoding || "utf8")); | ||
| } else { | ||
| callback(""); | ||
| } | ||
| }); | ||
| } else { | ||
| callback(""); | ||
| } | ||
| }, | ||
| /** | ||
| * Remove the entry from the file or the entry and all it's nested directories and files if the given entry is a directory | ||
| * | ||
| * @param {ZipEntry|string} entry | ||
| * @returns {void} | ||
| */ | ||
| deleteFile: function (entry, withsubfolders = true) { | ||
| // @TODO: test deleteFile | ||
| var item = getEntry(entry); | ||
| if (item) { | ||
| _zip.deleteFile(item.entryName, withsubfolders); | ||
| } | ||
| }, | ||
| /** | ||
| * Remove the entry from the file or directory without affecting any nested entries | ||
| * | ||
| * @param {ZipEntry|string} entry | ||
| * @returns {void} | ||
| */ | ||
| deleteEntry: function (entry) { | ||
| // @TODO: test deleteEntry | ||
| var item = getEntry(entry); | ||
| if (item) { | ||
| _zip.deleteEntry(item.entryName); | ||
| } | ||
| }, | ||
| /** | ||
| * Adds a comment to the zip. The zip must be rewritten after adding the comment. | ||
| * | ||
| * @param {string} comment | ||
| */ | ||
| addZipComment: function (comment) { | ||
| // @TODO: test addZipComment | ||
| _zip.comment = comment; | ||
| }, | ||
| /** | ||
| * Returns the zip comment | ||
| * | ||
| * @return String | ||
| */ | ||
| getZipComment: function () { | ||
| return _zip.comment || ""; | ||
| }, | ||
| /** | ||
| * Adds a comment to a specified zipEntry. The zip must be rewritten after adding the comment | ||
| * The comment cannot exceed 65535 characters in length | ||
| * | ||
| * @param {ZipEntry} entry | ||
| * @param {string} comment | ||
| */ | ||
| addZipEntryComment: function (entry, comment) { | ||
| var item = getEntry(entry); | ||
| if (item) { | ||
| item.comment = comment; | ||
| } | ||
| }, | ||
| /** | ||
| * Returns the comment of the specified entry | ||
| * | ||
| * @param {ZipEntry} entry | ||
| * @return String | ||
| */ | ||
| getZipEntryComment: function (entry) { | ||
| var item = getEntry(entry); | ||
| if (item) { | ||
| return item.comment || ""; | ||
| } | ||
| return ""; | ||
| }, | ||
| /** | ||
| * Updates the content of an existing entry inside the archive. The zip must be rewritten after updating the content | ||
| * | ||
| * @param {ZipEntry} entry | ||
| * @param {Buffer} content | ||
| */ | ||
| updateFile: function (entry, content) { | ||
| var item = getEntry(entry); | ||
| if (item) { | ||
| item.setData(content); | ||
| } | ||
| }, | ||
| /** | ||
| * Adds a file from the disk to the archive | ||
| * | ||
| * @param {string} localPath File to add to zip | ||
| * @param {string} [zipPath] Optional path inside the zip | ||
| * @param {string} [zipName] Optional name for the file | ||
| * @param {string} [comment] Optional file comment | ||
| */ | ||
| addLocalFile: function (localPath, zipPath, zipName, comment) { | ||
| if (filetools.fs.existsSync(localPath)) { | ||
| // fix ZipPath | ||
| zipPath = zipPath ? fixPath(zipPath) : ""; | ||
| // p - local file name | ||
| const p = pth.win32.basename(pth.win32.normalize(localPath)); | ||
| // add file name into zippath | ||
| zipPath += zipName ? zipName : p; | ||
| // read file attributes | ||
| const _attr = filetools.fs.statSync(localPath); | ||
| // get file content | ||
| const data = _attr.isFile() ? filetools.fs.readFileSync(localPath) : Buffer.alloc(0); | ||
| // if folder | ||
| if (_attr.isDirectory()) zipPath += filetools.sep; | ||
| // add file into zip file | ||
| this.addFile(zipPath, data, comment, _attr); | ||
| } else { | ||
| throw Utils.Errors.FILE_NOT_FOUND(localPath); | ||
| } | ||
| }, | ||
| /** | ||
| * Callback for showing if everything was done. | ||
| * | ||
| * @callback doneCallback | ||
| * @param {Error} err - Error object | ||
| * @param {boolean} done - was request fully completed | ||
| */ | ||
| /** | ||
| * Adds a file from the disk to the archive | ||
| * | ||
| * @param {(object|string)} options - options object, if it is string it us used as localPath. | ||
| * @param {string} options.localPath - Local path to the file. | ||
| * @param {string} [options.comment] - Optional file comment. | ||
| * @param {string} [options.zipPath] - Optional path inside the zip | ||
| * @param {string} [options.zipName] - Optional name for the file | ||
| * @param {doneCallback} callback - The callback that handles the response. | ||
| */ | ||
| addLocalFileAsync: function (options, callback) { | ||
| options = typeof options === "object" ? options : { localPath: options }; | ||
| const localPath = pth.resolve(options.localPath); | ||
| const { comment } = options; | ||
| let { zipPath, zipName } = options; | ||
| const self = this; | ||
| filetools.fs.stat(localPath, function (err, stats) { | ||
| if (err) return callback(err, false); | ||
| // fix ZipPath | ||
| zipPath = zipPath ? fixPath(zipPath) : ""; | ||
| // p - local file name | ||
| const p = pth.win32.basename(pth.win32.normalize(localPath)); | ||
| // add file name into zippath | ||
| zipPath += zipName ? zipName : p; | ||
| if (stats.isFile()) { | ||
| filetools.fs.readFile(localPath, function (err, data) { | ||
| if (err) return callback(err, false); | ||
| self.addFile(zipPath, data, comment, stats); | ||
| return setImmediate(callback, undefined, true); | ||
| }); | ||
| } else if (stats.isDirectory()) { | ||
| zipPath += filetools.sep; | ||
| self.addFile(zipPath, Buffer.alloc(0), comment, stats); | ||
| return setImmediate(callback, undefined, true); | ||
| } | ||
| }); | ||
| }, | ||
| /** | ||
| * Adds a local directory and all its nested files and directories to the archive | ||
| * | ||
| * @param {string} localPath - local path to the folder | ||
| * @param {string} [zipPath] - optional path inside zip | ||
| * @param {(RegExp|function)} [filter] - optional RegExp or Function if files match will be included. | ||
| */ | ||
| addLocalFolder: function (localPath, zipPath, filter) { | ||
| // Prepare filter | ||
| filter = filenameFilter(filter); | ||
| // fix ZipPath | ||
| zipPath = zipPath ? fixPath(zipPath) : ""; | ||
| // normalize the path first | ||
| localPath = pth.normalize(localPath); | ||
| if (filetools.fs.existsSync(localPath)) { | ||
| const items = filetools.findFiles(localPath); | ||
| const self = this; | ||
| if (items.length) { | ||
| for (const filepath of items) { | ||
| const p = pth.join(zipPath, relativePath(localPath, filepath)); | ||
| if (filter(p)) { | ||
| self.addLocalFile(filepath, pth.dirname(p)); | ||
| } | ||
| } | ||
| } | ||
| } else { | ||
| throw Utils.Errors.FILE_NOT_FOUND(localPath); | ||
| } | ||
| }, | ||
| /** | ||
| * Asynchronous addLocalFolder | ||
| * @param {string} localPath | ||
| * @param {callback} callback | ||
| * @param {string} [zipPath] optional path inside zip | ||
| * @param {RegExp|function} [filter] optional RegExp or Function if files match will | ||
| * be included. | ||
| */ | ||
| addLocalFolderAsync: function (localPath, callback, zipPath, filter) { | ||
| // Prepare filter | ||
| filter = filenameFilter(filter); | ||
| // fix ZipPath | ||
| zipPath = zipPath ? fixPath(zipPath) : ""; | ||
| // normalize the path first | ||
| localPath = pth.normalize(localPath); | ||
| var self = this; | ||
| filetools.fs.open(localPath, "r", function (err) { | ||
| if (err && err.code === "ENOENT") { | ||
| callback(undefined, Utils.Errors.FILE_NOT_FOUND(localPath)); | ||
| } else if (err) { | ||
| callback(undefined, err); | ||
| } else { | ||
| var items = filetools.findFiles(localPath); | ||
| var i = -1; | ||
| var next = function () { | ||
| i += 1; | ||
| if (i < items.length) { | ||
| var filepath = items[i]; | ||
| var p = relativePath(localPath, filepath).split("\\").join("/"); //windows fix | ||
| p = p | ||
| .normalize("NFD") | ||
| .replace(/[\u0300-\u036f]/g, "") | ||
| .replace(/[^\x20-\x7E]/g, ""); // accent fix | ||
| if (filter(p)) { | ||
| filetools.fs.stat(filepath, function (er0, stats) { | ||
| if (er0) callback(undefined, er0); | ||
| if (stats.isFile()) { | ||
| filetools.fs.readFile(filepath, function (er1, data) { | ||
| if (er1) { | ||
| callback(undefined, er1); | ||
| } else { | ||
| self.addFile(zipPath + p, data, "", stats); | ||
| next(); | ||
| } | ||
| }); | ||
| } else { | ||
| self.addFile(zipPath + p + "/", Buffer.alloc(0), "", stats); | ||
| next(); | ||
| } | ||
| }); | ||
| } else { | ||
| process.nextTick(() => { | ||
| next(); | ||
| }); | ||
| } | ||
| } else { | ||
| callback(true, undefined); | ||
| } | ||
| }; | ||
| next(); | ||
| } | ||
| }); | ||
| }, | ||
| /** | ||
| * Adds a local directory and all its nested files and directories to the archive | ||
| * | ||
| * @param {object | string} options - options object, if it is string it us used as localPath. | ||
| * @param {string} options.localPath - Local path to the folder. | ||
| * @param {string} [options.zipPath] - optional path inside zip. | ||
| * @param {RegExp|function} [options.filter] - optional RegExp or Function if files match will be included. | ||
| * @param {function|string} [options.namefix] - optional function to help fix filename | ||
| * @param {doneCallback} callback - The callback that handles the response. | ||
| * | ||
| */ | ||
| addLocalFolderAsync2: function (options, callback) { | ||
| const self = this; | ||
| options = typeof options === "object" ? options : { localPath: options }; | ||
| localPath = pth.resolve(fixPath(options.localPath)); | ||
| let { zipPath, filter, namefix } = options; | ||
| if (filter instanceof RegExp) { | ||
| filter = (function (rx) { | ||
| return function (filename) { | ||
| return rx.test(filename); | ||
| }; | ||
| })(filter); | ||
| } else if ("function" !== typeof filter) { | ||
| filter = function () { | ||
| return true; | ||
| }; | ||
| } | ||
| // fix ZipPath | ||
| zipPath = zipPath ? fixPath(zipPath) : ""; | ||
| // Check Namefix function | ||
| if (namefix == "latin1") { | ||
| namefix = (str) => | ||
| str | ||
| .normalize("NFD") | ||
| .replace(/[\u0300-\u036f]/g, "") | ||
| .replace(/[^\x20-\x7E]/g, ""); // accent fix (latin1 characers only) | ||
| } | ||
| if (typeof namefix !== "function") namefix = (str) => str; | ||
| // internal, create relative path + fix the name | ||
| const relPathFix = (entry) => pth.join(zipPath, namefix(relativePath(localPath, entry))); | ||
| const fileNameFix = (entry) => pth.win32.basename(pth.win32.normalize(namefix(entry))); | ||
| filetools.fs.open(localPath, "r", function (err) { | ||
| if (err && err.code === "ENOENT") { | ||
| callback(undefined, Utils.Errors.FILE_NOT_FOUND(localPath)); | ||
| } else if (err) { | ||
| callback(undefined, err); | ||
| } else { | ||
| filetools.findFilesAsync(localPath, function (err, fileEntries) { | ||
| if (err) return callback(err); | ||
| fileEntries = fileEntries.filter((dir) => filter(relPathFix(dir))); | ||
| if (!fileEntries.length) callback(undefined, false); | ||
| setImmediate( | ||
| fileEntries.reverse().reduce(function (next, entry) { | ||
| return function (err, done) { | ||
| if (err || done === false) return setImmediate(next, err, false); | ||
| self.addLocalFileAsync( | ||
| { | ||
| localPath: entry, | ||
| zipPath: pth.dirname(relPathFix(entry)), | ||
| zipName: fileNameFix(entry) | ||
| }, | ||
| next | ||
| ); | ||
| }; | ||
| }, callback) | ||
| ); | ||
| }); | ||
| } | ||
| }); | ||
| }, | ||
| /** | ||
| * Adds a local directory and all its nested files and directories to the archive | ||
| * | ||
| * @param {string} localPath - path where files will be extracted | ||
| * @param {object} props - optional properties | ||
| * @param {string} [props.zipPath] - optional path inside zip | ||
| * @param {RegExp|function} [props.filter] - optional RegExp or Function if files match will be included. | ||
| * @param {function|string} [props.namefix] - optional function to help fix filename | ||
| */ | ||
| addLocalFolderPromise: function (localPath, props) { | ||
| return new Promise((resolve, reject) => { | ||
| this.addLocalFolderAsync2(Object.assign({ localPath }, props), (err, done) => { | ||
| if (err) reject(err); | ||
| if (done) resolve(this); | ||
| }); | ||
| }); | ||
| }, | ||
| /** | ||
| * Allows you to create a entry (file or directory) in the zip file. | ||
| * If you want to create a directory the entryName must end in / and a null buffer should be provided. | ||
| * Comment and attributes are optional | ||
| * | ||
| * @param {string} entryName | ||
| * @param {Buffer | string} content - file content as buffer or utf8 coded string | ||
| * @param {string} [comment] - file comment | ||
| * @param {number | object} [attr] - number as unix file permissions, object as filesystem Stats object | ||
| */ | ||
| addFile: function (entryName, content, comment, attr) { | ||
| entryName = zipnamefix(entryName); | ||
| let entry = getEntry(entryName); | ||
| const update = entry != null; | ||
| // prepare new entry | ||
| if (!update) { | ||
| entry = new ZipEntry(opts); | ||
| entry.entryName = entryName; | ||
| } | ||
| entry.comment = comment || ""; | ||
| const isStat = "object" === typeof attr && attr instanceof filetools.fs.Stats; | ||
| // last modification time from file stats | ||
| if (isStat) { | ||
| entry.header.time = attr.mtime; | ||
| } | ||
| // Set file attribute | ||
| var fileattr = entry.isDirectory ? 0x10 : 0; // (MS-DOS directory flag) | ||
| // extended attributes field for Unix | ||
| // set file type either S_IFDIR / S_IFREG | ||
| let unix = entry.isDirectory ? 0x4000 : 0x8000; | ||
| if (isStat) { | ||
| // File attributes from file stats | ||
| unix |= 0xfff & attr.mode; | ||
| } else if ("number" === typeof attr) { | ||
| // attr from given attr values | ||
| unix |= 0xfff & attr; | ||
| } else { | ||
| // Default values: | ||
| unix |= entry.isDirectory ? 0o755 : 0o644; // permissions (drwxr-xr-x) or (-r-wr--r--) | ||
| } | ||
| fileattr = (fileattr | (unix << 16)) >>> 0; // add attributes | ||
| entry.attr = fileattr; | ||
| entry.setData(content); | ||
| if (!update) _zip.setEntry(entry); | ||
| return entry; | ||
| }, | ||
| /** | ||
| * Returns an array of ZipEntry objects representing the files and folders inside the archive | ||
| * | ||
| * @param {string} [password] | ||
| * @returns Array | ||
| */ | ||
| getEntries: function (password) { | ||
| _zip.password = password; | ||
| return _zip ? _zip.entries : []; | ||
| }, | ||
| /** | ||
| * Returns a ZipEntry object representing the file or folder specified by ``name``. | ||
| * | ||
| * @param {string} name | ||
| * @return ZipEntry | ||
| */ | ||
| getEntry: function (/**String*/ name) { | ||
| return getEntry(name); | ||
| }, | ||
| getEntryCount: function () { | ||
| return _zip.getEntryCount(); | ||
| }, | ||
| forEach: function (callback) { | ||
| return _zip.forEach(callback); | ||
| }, | ||
| /** | ||
| * Extracts the given entry to the given targetPath | ||
| * If the entry is a directory inside the archive, the entire directory and it's subdirectories will be extracted | ||
| * | ||
| * @param {string|ZipEntry} entry - ZipEntry object or String with the full path of the entry | ||
| * @param {string} targetPath - Target folder where to write the file | ||
| * @param {boolean} [maintainEntryPath=true] - If maintainEntryPath is true and the entry is inside a folder, the entry folder will be created in targetPath as well. Default is TRUE | ||
| * @param {boolean} [overwrite=false] - If the file already exists at the target path, the file will be overwriten if this is true. | ||
| * @param {boolean} [keepOriginalPermission=false] - The file will be set as the permission from the entry if this is true. | ||
| * @param {string} [outFileName] - String If set will override the filename of the extracted file (Only works if the entry is a file) | ||
| * | ||
| * @return Boolean | ||
| */ | ||
| extractEntryTo: function (entry, targetPath, maintainEntryPath, overwrite, keepOriginalPermission, outFileName) { | ||
| overwrite = get_Bool(false, overwrite); | ||
| keepOriginalPermission = get_Bool(false, keepOriginalPermission); | ||
| maintainEntryPath = get_Bool(true, maintainEntryPath); | ||
| outFileName = get_Str(keepOriginalPermission, outFileName); | ||
| var item = getEntry(entry); | ||
| if (!item) { | ||
| throw Utils.Errors.NO_ENTRY(); | ||
| } | ||
| var entryName = canonical(item.entryName); | ||
| var target = sanitize(targetPath, outFileName && !item.isDirectory ? outFileName : maintainEntryPath ? entryName : pth.basename(entryName)); | ||
| if (item.isDirectory) { | ||
| var children = _zip.getEntryChildren(item); | ||
| children.forEach(function (child) { | ||
| if (child.isDirectory) return; | ||
| var content = child.getData(); | ||
| if (!content) { | ||
| throw Utils.Errors.CANT_EXTRACT_FILE(); | ||
| } | ||
| var name = canonical(child.entryName); | ||
| var childName = sanitize(targetPath, maintainEntryPath ? name : pth.basename(name)); | ||
| // The reverse operation for attr depend on method addFile() | ||
| const fileAttr = keepOriginalPermission ? child.header.fileAttr : undefined; | ||
| filetools.writeFileTo(childName, content, overwrite, fileAttr); | ||
| }); | ||
| return true; | ||
| } | ||
| var content = item.getData(_zip.password); | ||
| if (!content) throw Utils.Errors.CANT_EXTRACT_FILE(); | ||
| if (filetools.fs.existsSync(target) && !overwrite) { | ||
| throw Utils.Errors.CANT_OVERRIDE(); | ||
| } | ||
| // The reverse operation for attr depend on method addFile() | ||
| const fileAttr = keepOriginalPermission ? entry.header.fileAttr : undefined; | ||
| filetools.writeFileTo(target, content, overwrite, fileAttr); | ||
| return true; | ||
| }, | ||
| /** | ||
| * Test the archive | ||
| * @param {string} [pass] | ||
| */ | ||
| test: function (pass) { | ||
| if (!_zip) { | ||
| return false; | ||
| } | ||
| for (var entry in _zip.entries) { | ||
| try { | ||
| if (entry.isDirectory) { | ||
| continue; | ||
| } | ||
| var content = _zip.entries[entry].getData(pass); | ||
| if (!content) { | ||
| return false; | ||
| } | ||
| } catch (err) { | ||
| return false; | ||
| } | ||
| } | ||
| return true; | ||
| }, | ||
| /** | ||
| * Extracts the entire archive to the given location | ||
| * | ||
| * @param {string} targetPath Target location | ||
| * @param {boolean} [overwrite=false] If the file already exists at the target path, the file will be overwriten if this is true. | ||
| * Default is FALSE | ||
| * @param {boolean} [keepOriginalPermission=false] The file will be set as the permission from the entry if this is true. | ||
| * Default is FALSE | ||
| * @param {string|Buffer} [pass] password | ||
| */ | ||
| extractAllTo: function (targetPath, overwrite, keepOriginalPermission, pass) { | ||
| keepOriginalPermission = get_Bool(false, keepOriginalPermission); | ||
| pass = get_Str(keepOriginalPermission, pass); | ||
| overwrite = get_Bool(false, overwrite); | ||
| if (!_zip) throw Utils.Errors.NO_ZIP(); | ||
| _zip.entries.forEach(function (entry) { | ||
| var entryName = sanitize(targetPath, canonical(entry.entryName)); | ||
| if (entry.isDirectory) { | ||
| filetools.makeDir(entryName); | ||
| return; | ||
| } | ||
| var content = entry.getData(pass); | ||
| if (!content) { | ||
| throw Utils.Errors.CANT_EXTRACT_FILE(); | ||
| } | ||
| // The reverse operation for attr depend on method addFile() | ||
| const fileAttr = keepOriginalPermission ? entry.header.fileAttr : undefined; | ||
| filetools.writeFileTo(entryName, content, overwrite, fileAttr); | ||
| try { | ||
| filetools.fs.utimesSync(entryName, entry.header.time, entry.header.time); | ||
| } catch (err) { | ||
| throw Utils.Errors.CANT_EXTRACT_FILE(); | ||
| } | ||
| }); | ||
| }, | ||
| /** | ||
| * Asynchronous extractAllTo | ||
| * | ||
| * @param {string} targetPath Target location | ||
| * @param {boolean} [overwrite=false] If the file already exists at the target path, the file will be overwriten if this is true. | ||
| * Default is FALSE | ||
| * @param {boolean} [keepOriginalPermission=false] The file will be set as the permission from the entry if this is true. | ||
| * Default is FALSE | ||
| * @param {function} callback The callback will be executed when all entries are extracted successfully or any error is thrown. | ||
| */ | ||
| extractAllToAsync: function (targetPath, overwrite, keepOriginalPermission, callback) { | ||
| callback = get_Fun(overwrite, keepOriginalPermission, callback); | ||
| keepOriginalPermission = get_Bool(false, keepOriginalPermission); | ||
| overwrite = get_Bool(false, overwrite); | ||
| if (!callback) { | ||
| return new Promise((resolve, reject) => { | ||
| this.extractAllToAsync(targetPath, overwrite, keepOriginalPermission, function (err) { | ||
| if (err) { | ||
| reject(err); | ||
| } else { | ||
| resolve(this); | ||
| } | ||
| }); | ||
| }); | ||
| } | ||
| if (!_zip) { | ||
| callback(Utils.Errors.NO_ZIP()); | ||
| return; | ||
| } | ||
| targetPath = pth.resolve(targetPath); | ||
| // convert entryName to | ||
| const getPath = (entry) => sanitize(targetPath, pth.normalize(canonical(entry.entryName))); | ||
| const getError = (msg, file) => new Error(msg + ': "' + file + '"'); | ||
| // separate directories from files | ||
| const dirEntries = []; | ||
| const fileEntries = []; | ||
| _zip.entries.forEach((e) => { | ||
| if (e.isDirectory) { | ||
| dirEntries.push(e); | ||
| } else { | ||
| fileEntries.push(e); | ||
| } | ||
| }); | ||
| // Create directory entries first synchronously | ||
| // this prevents race condition and assures folders are there before writing files | ||
| for (const entry of dirEntries) { | ||
| const dirPath = getPath(entry); | ||
| // The reverse operation for attr depend on method addFile() | ||
| const dirAttr = keepOriginalPermission ? entry.header.fileAttr : undefined; | ||
| try { | ||
| filetools.makeDir(dirPath); | ||
| if (dirAttr) filetools.fs.chmodSync(dirPath, dirAttr); | ||
| // in unix timestamp will change if files are later added to folder, but still | ||
| filetools.fs.utimesSync(dirPath, entry.header.time, entry.header.time); | ||
| } catch (er) { | ||
| callback(getError("Unable to create folder", dirPath)); | ||
| } | ||
| } | ||
| fileEntries.reverse().reduce(function (next, entry) { | ||
| return function (err) { | ||
| if (err) { | ||
| next(err); | ||
| } else { | ||
| const entryName = pth.normalize(canonical(entry.entryName)); | ||
| const filePath = sanitize(targetPath, entryName); | ||
| entry.getDataAsync(function (content, err_1) { | ||
| if (err_1) { | ||
| next(err_1); | ||
| } else if (!content) { | ||
| next(Utils.Errors.CANT_EXTRACT_FILE()); | ||
| } else { | ||
| // The reverse operation for attr depend on method addFile() | ||
| const fileAttr = keepOriginalPermission ? entry.header.fileAttr : undefined; | ||
| filetools.writeFileToAsync(filePath, content, overwrite, fileAttr, function (succ) { | ||
| if (!succ) { | ||
| next(getError("Unable to write file", filePath)); | ||
| } | ||
| filetools.fs.utimes(filePath, entry.header.time, entry.header.time, function (err_2) { | ||
| if (err_2) { | ||
| next(getError("Unable to set times", filePath)); | ||
| } else { | ||
| next(); | ||
| } | ||
| }); | ||
| }); | ||
| } | ||
| }); | ||
| } | ||
| }; | ||
| }, callback)(); | ||
| }, | ||
| /** | ||
| * Writes the newly created zip file to disk at the specified location or if a zip was opened and no ``targetFileName`` is provided, it will overwrite the opened zip | ||
| * | ||
| * @param {string} targetFileName | ||
| * @param {function} callback | ||
| */ | ||
| writeZip: function (targetFileName, callback) { | ||
| if (arguments.length === 1) { | ||
| if (typeof targetFileName === "function") { | ||
| callback = targetFileName; | ||
| targetFileName = ""; | ||
| } | ||
| } | ||
| if (!targetFileName && opts.filename) { | ||
| targetFileName = opts.filename; | ||
| } | ||
| if (!targetFileName) return; | ||
| var zipData = _zip.compressToBuffer(); | ||
| if (zipData) { | ||
| var ok = filetools.writeFileTo(targetFileName, zipData, true); | ||
| if (typeof callback === "function") callback(!ok ? new Error("failed") : null, ""); | ||
| } | ||
| }, | ||
| /** | ||
| * | ||
| * @param {string} targetFileName | ||
| * @param {object} [props] | ||
| * @param {boolean} [props.overwrite=true] If the file already exists at the target path, the file will be overwriten if this is true. | ||
| * @param {boolean} [props.perm] The file will be set as the permission from the entry if this is true. | ||
| * @returns {Promise<void>} | ||
| */ | ||
| writeZipPromise: function (/**String*/ targetFileName, /* object */ props) { | ||
| const { overwrite, perm } = Object.assign({ overwrite: true }, props); | ||
| return new Promise((resolve, reject) => { | ||
| // find file name | ||
| if (!targetFileName && opts.filename) targetFileName = opts.filename; | ||
| if (!targetFileName) reject("ADM-ZIP: ZIP File Name Missing"); | ||
| this.toBufferPromise().then((zipData) => { | ||
| const ret = (done) => (done ? resolve(done) : reject("ADM-ZIP: Wasn't able to write zip file")); | ||
| filetools.writeFileToAsync(targetFileName, zipData, overwrite, perm, ret); | ||
| }, reject); | ||
| }); | ||
| }, | ||
| /** | ||
| * @returns {Promise<Buffer>} A promise to the Buffer. | ||
| */ | ||
| toBufferPromise: function () { | ||
| return new Promise((resolve, reject) => { | ||
| _zip.toAsyncBuffer(resolve, reject); | ||
| }); | ||
| }, | ||
| /** | ||
| * Returns the content of the entire zip file as a Buffer object | ||
| * | ||
| * @prop {function} [onSuccess] | ||
| * @prop {function} [onFail] | ||
| * @prop {function} [onItemStart] | ||
| * @prop {function} [onItemEnd] | ||
| * @returns {Buffer} | ||
| */ | ||
| toBuffer: function (onSuccess, onFail, onItemStart, onItemEnd) { | ||
| if (typeof onSuccess === "function") { | ||
| _zip.toAsyncBuffer(onSuccess, onFail, onItemStart, onItemEnd); | ||
| return null; | ||
| } | ||
| return _zip.compressToBuffer(); | ||
| } | ||
| }; | ||
| }; |
| var Utils = require("../util"), | ||
| Constants = Utils.Constants; | ||
| /* The central directory file header */ | ||
| module.exports = function () { | ||
| var _verMade = 20, // v2.0 | ||
| _version = 10, // v1.0 | ||
| _flags = 0, | ||
| _method = 0, | ||
| _time = 0, | ||
| _crc = 0, | ||
| _compressedSize = 0, | ||
| _size = 0, | ||
| _fnameLen = 0, | ||
| _extraLen = 0, | ||
| _comLen = 0, | ||
| _diskStart = 0, | ||
| _inattr = 0, | ||
| _attr = 0, | ||
| _offset = 0; | ||
| _verMade |= Utils.isWin ? 0x0a00 : 0x0300; | ||
| // Set EFS flag since filename and comment fields are all by default encoded using UTF-8. | ||
| // Without it file names may be corrupted for other apps when file names use unicode chars | ||
| _flags |= Constants.FLG_EFS; | ||
| const _localHeader = { | ||
| extraLen: 0 | ||
| }; | ||
| // casting | ||
| const uint32 = (val) => Math.max(0, val) >>> 0; | ||
| const uint16 = (val) => Math.max(0, val) & 0xffff; | ||
| const uint8 = (val) => Math.max(0, val) & 0xff; | ||
| _time = Utils.fromDate2DOS(new Date()); | ||
| return { | ||
| get made() { | ||
| return _verMade; | ||
| }, | ||
| set made(val) { | ||
| _verMade = val; | ||
| }, | ||
| get version() { | ||
| return _version; | ||
| }, | ||
| set version(val) { | ||
| _version = val; | ||
| }, | ||
| get flags() { | ||
| return _flags; | ||
| }, | ||
| set flags(val) { | ||
| _flags = val; | ||
| }, | ||
| get flags_efs() { | ||
| return (_flags & Constants.FLG_EFS) > 0; | ||
| }, | ||
| set flags_efs(val) { | ||
| if (val) { | ||
| _flags |= Constants.FLG_EFS; | ||
| } else { | ||
| _flags &= ~Constants.FLG_EFS; | ||
| } | ||
| }, | ||
| get flags_desc() { | ||
| return (_flags & Constants.FLG_DESC) > 0; | ||
| }, | ||
| set flags_desc(val) { | ||
| if (val) { | ||
| _flags |= Constants.FLG_DESC; | ||
| } else { | ||
| _flags &= ~Constants.FLG_DESC; | ||
| } | ||
| }, | ||
| get method() { | ||
| return _method; | ||
| }, | ||
| set method(val) { | ||
| switch (val) { | ||
| case Constants.STORED: | ||
| this.version = 10; | ||
| case Constants.DEFLATED: | ||
| default: | ||
| this.version = 20; | ||
| } | ||
| _method = val; | ||
| }, | ||
| get time() { | ||
| return Utils.fromDOS2Date(this.timeval); | ||
| }, | ||
| set time(val) { | ||
| val = new Date(val); | ||
| this.timeval = Utils.fromDate2DOS(val); | ||
| }, | ||
| get timeval() { | ||
| return _time; | ||
| }, | ||
| set timeval(val) { | ||
| _time = uint32(val); | ||
| }, | ||
| get timeHighByte() { | ||
| return uint8(_time >>> 8); | ||
| }, | ||
| get crc() { | ||
| return _crc; | ||
| }, | ||
| set crc(val) { | ||
| _crc = uint32(val); | ||
| }, | ||
| get compressedSize() { | ||
| return _compressedSize; | ||
| }, | ||
| set compressedSize(val) { | ||
| _compressedSize = uint32(val); | ||
| }, | ||
| get size() { | ||
| return _size; | ||
| }, | ||
| set size(val) { | ||
| _size = uint32(val); | ||
| }, | ||
| get fileNameLength() { | ||
| return _fnameLen; | ||
| }, | ||
| set fileNameLength(val) { | ||
| _fnameLen = val; | ||
| }, | ||
| get extraLength() { | ||
| return _extraLen; | ||
| }, | ||
| set extraLength(val) { | ||
| _extraLen = val; | ||
| }, | ||
| get extraLocalLength() { | ||
| return _localHeader.extraLen; | ||
| }, | ||
| set extraLocalLength(val) { | ||
| _localHeader.extraLen = val; | ||
| }, | ||
| get commentLength() { | ||
| return _comLen; | ||
| }, | ||
| set commentLength(val) { | ||
| _comLen = val; | ||
| }, | ||
| get diskNumStart() { | ||
| return _diskStart; | ||
| }, | ||
| set diskNumStart(val) { | ||
| _diskStart = uint32(val); | ||
| }, | ||
| get inAttr() { | ||
| return _inattr; | ||
| }, | ||
| set inAttr(val) { | ||
| _inattr = uint32(val); | ||
| }, | ||
| get attr() { | ||
| return _attr; | ||
| }, | ||
| set attr(val) { | ||
| _attr = uint32(val); | ||
| }, | ||
| // get Unix file permissions | ||
| get fileAttr() { | ||
| return (_attr || 0) >> 16 & 0xfff; | ||
| }, | ||
| get offset() { | ||
| return _offset; | ||
| }, | ||
| set offset(val) { | ||
| _offset = uint32(val); | ||
| }, | ||
| get encrypted() { | ||
| return (_flags & Constants.FLG_ENC) === Constants.FLG_ENC; | ||
| }, | ||
| get centralHeaderSize() { | ||
| return Constants.CENHDR + _fnameLen + _extraLen + _comLen; | ||
| }, | ||
| get realDataOffset() { | ||
| return _offset + Constants.LOCHDR + _localHeader.fnameLen + _localHeader.extraLen; | ||
| }, | ||
| get localHeader() { | ||
| return _localHeader; | ||
| }, | ||
| loadLocalHeaderFromBinary: function (/*Buffer*/ input) { | ||
| var data = input.slice(_offset, _offset + Constants.LOCHDR); | ||
| // 30 bytes and should start with "PK\003\004" | ||
| if (data.readUInt32LE(0) !== Constants.LOCSIG) { | ||
| throw Utils.Errors.INVALID_LOC(); | ||
| } | ||
| // version needed to extract | ||
| _localHeader.version = data.readUInt16LE(Constants.LOCVER); | ||
| // general purpose bit flag | ||
| _localHeader.flags = data.readUInt16LE(Constants.LOCFLG); | ||
| // desc flag | ||
| _localHeader.flags_desc = (_localHeader.flags & Constants.FLG_DESC) > 0; | ||
| // compression method | ||
| _localHeader.method = data.readUInt16LE(Constants.LOCHOW); | ||
| // modification time (2 bytes time, 2 bytes date) | ||
| _localHeader.time = data.readUInt32LE(Constants.LOCTIM); | ||
| // uncompressed file crc-32 valu | ||
| _localHeader.crc = data.readUInt32LE(Constants.LOCCRC); | ||
| // compressed size | ||
| _localHeader.compressedSize = data.readUInt32LE(Constants.LOCSIZ); | ||
| // uncompressed size | ||
| _localHeader.size = data.readUInt32LE(Constants.LOCLEN); | ||
| // filename length | ||
| _localHeader.fnameLen = data.readUInt16LE(Constants.LOCNAM); | ||
| // extra field length | ||
| _localHeader.extraLen = data.readUInt16LE(Constants.LOCEXT); | ||
| // read extra data | ||
| const extraStart = _offset + Constants.LOCHDR + _localHeader.fnameLen; | ||
| const extraEnd = extraStart + _localHeader.extraLen; | ||
| return input.slice(extraStart, extraEnd); | ||
| }, | ||
| loadFromBinary: function (/*Buffer*/ data) { | ||
| // data should be 46 bytes and start with "PK 01 02" | ||
| if (data.length !== Constants.CENHDR || data.readUInt32LE(0) !== Constants.CENSIG) { | ||
| throw Utils.Errors.INVALID_CEN(); | ||
| } | ||
| // version made by | ||
| _verMade = data.readUInt16LE(Constants.CENVEM); | ||
| // version needed to extract | ||
| _version = data.readUInt16LE(Constants.CENVER); | ||
| // encrypt, decrypt flags | ||
| _flags = data.readUInt16LE(Constants.CENFLG); | ||
| // compression method | ||
| _method = data.readUInt16LE(Constants.CENHOW); | ||
| // modification time (2 bytes time, 2 bytes date) | ||
| _time = data.readUInt32LE(Constants.CENTIM); | ||
| // uncompressed file crc-32 value | ||
| _crc = data.readUInt32LE(Constants.CENCRC); | ||
| // compressed size | ||
| _compressedSize = data.readUInt32LE(Constants.CENSIZ); | ||
| // uncompressed size | ||
| _size = data.readUInt32LE(Constants.CENLEN); | ||
| // filename length | ||
| _fnameLen = data.readUInt16LE(Constants.CENNAM); | ||
| // extra field length | ||
| _extraLen = data.readUInt16LE(Constants.CENEXT); | ||
| // file comment length | ||
| _comLen = data.readUInt16LE(Constants.CENCOM); | ||
| // volume number start | ||
| _diskStart = data.readUInt16LE(Constants.CENDSK); | ||
| // internal file attributes | ||
| _inattr = data.readUInt16LE(Constants.CENATT); | ||
| // external file attributes | ||
| _attr = data.readUInt32LE(Constants.CENATX); | ||
| // LOC header offset | ||
| _offset = data.readUInt32LE(Constants.CENOFF); | ||
| }, | ||
| localHeaderToBinary: function () { | ||
| // LOC header size (30 bytes) | ||
| var data = Buffer.alloc(Constants.LOCHDR); | ||
| // "PK\003\004" | ||
| data.writeUInt32LE(Constants.LOCSIG, 0); | ||
| // version needed to extract | ||
| data.writeUInt16LE(_version, Constants.LOCVER); | ||
| // general purpose bit flag | ||
| data.writeUInt16LE(_flags, Constants.LOCFLG); | ||
| // compression method | ||
| data.writeUInt16LE(_method, Constants.LOCHOW); | ||
| // modification time (2 bytes time, 2 bytes date) | ||
| data.writeUInt32LE(_time, Constants.LOCTIM); | ||
| // uncompressed file crc-32 value | ||
| data.writeUInt32LE(_crc, Constants.LOCCRC); | ||
| // compressed size | ||
| data.writeUInt32LE(_compressedSize, Constants.LOCSIZ); | ||
| // uncompressed size | ||
| data.writeUInt32LE(_size, Constants.LOCLEN); | ||
| // filename length | ||
| data.writeUInt16LE(_fnameLen, Constants.LOCNAM); | ||
| // extra field length | ||
| data.writeUInt16LE(_localHeader.extraLen, Constants.LOCEXT); | ||
| return data; | ||
| }, | ||
| centralHeaderToBinary: function () { | ||
| // CEN header size (46 bytes) | ||
| var data = Buffer.alloc(Constants.CENHDR + _fnameLen + _extraLen + _comLen); | ||
| // "PK\001\002" | ||
| data.writeUInt32LE(Constants.CENSIG, 0); | ||
| // version made by | ||
| data.writeUInt16LE(_verMade, Constants.CENVEM); | ||
| // version needed to extract | ||
| data.writeUInt16LE(_version, Constants.CENVER); | ||
| // encrypt, decrypt flags | ||
| data.writeUInt16LE(_flags, Constants.CENFLG); | ||
| // compression method | ||
| data.writeUInt16LE(_method, Constants.CENHOW); | ||
| // modification time (2 bytes time, 2 bytes date) | ||
| data.writeUInt32LE(_time, Constants.CENTIM); | ||
| // uncompressed file crc-32 value | ||
| data.writeUInt32LE(_crc, Constants.CENCRC); | ||
| // compressed size | ||
| data.writeUInt32LE(_compressedSize, Constants.CENSIZ); | ||
| // uncompressed size | ||
| data.writeUInt32LE(_size, Constants.CENLEN); | ||
| // filename length | ||
| data.writeUInt16LE(_fnameLen, Constants.CENNAM); | ||
| // extra field length | ||
| data.writeUInt16LE(_extraLen, Constants.CENEXT); | ||
| // file comment length | ||
| data.writeUInt16LE(_comLen, Constants.CENCOM); | ||
| // volume number start | ||
| data.writeUInt16LE(_diskStart, Constants.CENDSK); | ||
| // internal file attributes | ||
| data.writeUInt16LE(_inattr, Constants.CENATT); | ||
| // external file attributes | ||
| data.writeUInt32LE(_attr, Constants.CENATX); | ||
| // LOC header offset | ||
| data.writeUInt32LE(_offset, Constants.CENOFF); | ||
| return data; | ||
| }, | ||
| toJSON: function () { | ||
| const bytes = function (nr) { | ||
| return nr + " bytes"; | ||
| }; | ||
| return { | ||
| made: _verMade, | ||
| version: _version, | ||
| flags: _flags, | ||
| method: Utils.methodToString(_method), | ||
| time: this.time, | ||
| crc: "0x" + _crc.toString(16).toUpperCase(), | ||
| compressedSize: bytes(_compressedSize), | ||
| size: bytes(_size), | ||
| fileNameLength: bytes(_fnameLen), | ||
| extraLength: bytes(_extraLen), | ||
| commentLength: bytes(_comLen), | ||
| diskNumStart: _diskStart, | ||
| inAttr: _inattr, | ||
| attr: _attr, | ||
| offset: _offset, | ||
| centralHeaderSize: bytes(Constants.CENHDR + _fnameLen + _extraLen + _comLen) | ||
| }; | ||
| }, | ||
| toString: function () { | ||
| return JSON.stringify(this.toJSON(), null, "\t"); | ||
| } | ||
| }; | ||
| }; |
| exports.EntryHeader = require("./entryHeader"); | ||
| exports.MainHeader = require("./mainHeader"); |
| var Utils = require("../util"), | ||
| Constants = Utils.Constants; | ||
| /* The entries in the end of central directory */ | ||
| module.exports = function () { | ||
| var _volumeEntries = 0, | ||
| _totalEntries = 0, | ||
| _size = 0, | ||
| _offset = 0, | ||
| _commentLength = 0; | ||
| return { | ||
| get diskEntries() { | ||
| return _volumeEntries; | ||
| }, | ||
| set diskEntries(/*Number*/ val) { | ||
| _volumeEntries = _totalEntries = val; | ||
| }, | ||
| get totalEntries() { | ||
| return _totalEntries; | ||
| }, | ||
| set totalEntries(/*Number*/ val) { | ||
| _totalEntries = _volumeEntries = val; | ||
| }, | ||
| get size() { | ||
| return _size; | ||
| }, | ||
| set size(/*Number*/ val) { | ||
| _size = val; | ||
| }, | ||
| get offset() { | ||
| return _offset; | ||
| }, | ||
| set offset(/*Number*/ val) { | ||
| _offset = val; | ||
| }, | ||
| get commentLength() { | ||
| return _commentLength; | ||
| }, | ||
| set commentLength(/*Number*/ val) { | ||
| _commentLength = val; | ||
| }, | ||
| get mainHeaderSize() { | ||
| return Constants.ENDHDR + _commentLength; | ||
| }, | ||
| loadFromBinary: function (/*Buffer*/ data) { | ||
| // data should be 22 bytes and start with "PK 05 06" | ||
| // or be 56+ bytes and start with "PK 06 06" for Zip64 | ||
| if ( | ||
| (data.length !== Constants.ENDHDR || data.readUInt32LE(0) !== Constants.ENDSIG) && | ||
| (data.length < Constants.ZIP64HDR || data.readUInt32LE(0) !== Constants.ZIP64SIG) | ||
| ) { | ||
| throw Utils.Errors.INVALID_END(); | ||
| } | ||
| if (data.readUInt32LE(0) === Constants.ENDSIG) { | ||
| // number of entries on this volume | ||
| _volumeEntries = data.readUInt16LE(Constants.ENDSUB); | ||
| // total number of entries | ||
| _totalEntries = data.readUInt16LE(Constants.ENDTOT); | ||
| // central directory size in bytes | ||
| _size = data.readUInt32LE(Constants.ENDSIZ); | ||
| // offset of first CEN header | ||
| _offset = data.readUInt32LE(Constants.ENDOFF); | ||
| // zip file comment length | ||
| _commentLength = data.readUInt16LE(Constants.ENDCOM); | ||
| } else { | ||
| // number of entries on this volume | ||
| _volumeEntries = Utils.readBigUInt64LE(data, Constants.ZIP64SUB); | ||
| // total number of entries | ||
| _totalEntries = Utils.readBigUInt64LE(data, Constants.ZIP64TOT); | ||
| // central directory size in bytes | ||
| _size = Utils.readBigUInt64LE(data, Constants.ZIP64SIZE); | ||
| // offset of first CEN header | ||
| _offset = Utils.readBigUInt64LE(data, Constants.ZIP64OFF); | ||
| _commentLength = 0; | ||
| } | ||
| }, | ||
| toBinary: function () { | ||
| var b = Buffer.alloc(Constants.ENDHDR + _commentLength); | ||
| // "PK 05 06" signature | ||
| b.writeUInt32LE(Constants.ENDSIG, 0); | ||
| b.writeUInt32LE(0, 4); | ||
| // number of entries on this volume | ||
| b.writeUInt16LE(_volumeEntries, Constants.ENDSUB); | ||
| // total number of entries | ||
| b.writeUInt16LE(_totalEntries, Constants.ENDTOT); | ||
| // central directory size in bytes | ||
| b.writeUInt32LE(_size, Constants.ENDSIZ); | ||
| // offset of first CEN header | ||
| b.writeUInt32LE(_offset, Constants.ENDOFF); | ||
| // zip file comment length | ||
| b.writeUInt16LE(_commentLength, Constants.ENDCOM); | ||
| // fill comment memory with spaces so no garbage is left there | ||
| b.fill(" ", Constants.ENDHDR); | ||
| return b; | ||
| }, | ||
| toJSON: function () { | ||
| // creates 0x0000 style output | ||
| const offset = function (nr, len) { | ||
| let offs = nr.toString(16).toUpperCase(); | ||
| while (offs.length < len) offs = "0" + offs; | ||
| return "0x" + offs; | ||
| }; | ||
| return { | ||
| diskEntries: _volumeEntries, | ||
| totalEntries: _totalEntries, | ||
| size: _size + " bytes", | ||
| offset: offset(_offset, 4), | ||
| commentLength: _commentLength | ||
| }; | ||
| }, | ||
| toString: function () { | ||
| return JSON.stringify(this.toJSON(), null, "\t"); | ||
| } | ||
| }; | ||
| }; | ||
| // Misspelled |
| MIT License | ||
| Copyright (c) 2012 Another-D-Mention Software and other contributors | ||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| of this software and associated documentation files (the "Software"), to deal | ||
| in the Software without restriction, including without limitation the rights | ||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| copies of the Software, and to permit persons to whom the Software is | ||
| furnished to do so, subject to the following conditions: | ||
| The above copyright notice and this permission notice shall be included in all | ||
| copies or substantial portions of the Software. | ||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| SOFTWARE. |
| module.exports = function (/*Buffer*/ inbuf) { | ||
| var zlib = require("zlib"); | ||
| var opts = { chunkSize: (parseInt(inbuf.length / 1024) + 1) * 1024 }; | ||
| return { | ||
| deflate: function () { | ||
| return zlib.deflateRawSync(inbuf, opts); | ||
| }, | ||
| deflateAsync: function (/*Function*/ callback) { | ||
| var tmp = zlib.createDeflateRaw(opts), | ||
| parts = [], | ||
| total = 0; | ||
| tmp.on("data", function (data) { | ||
| parts.push(data); | ||
| total += data.length; | ||
| }); | ||
| tmp.on("end", function () { | ||
| var buf = Buffer.alloc(total), | ||
| written = 0; | ||
| buf.fill(0); | ||
| for (var i = 0; i < parts.length; i++) { | ||
| var part = parts[i]; | ||
| part.copy(buf, written); | ||
| written += part.length; | ||
| } | ||
| callback && callback(buf); | ||
| }); | ||
| tmp.end(inbuf); | ||
| } | ||
| }; | ||
| }; |
| exports.Deflater = require("./deflater"); | ||
| exports.Inflater = require("./inflater"); | ||
| exports.ZipCrypto = require("./zipcrypto"); |
| const version = +(process.versions ? process.versions.node : "").split(".")[0] || 0; | ||
| module.exports = function (/*Buffer*/ inbuf, /*number*/ expectedLength) { | ||
| var zlib = require("zlib"); | ||
| const option = version >= 15 && expectedLength > 0 ? { maxOutputLength: expectedLength } : {}; | ||
| return { | ||
| inflate: function () { | ||
| return zlib.inflateRawSync(inbuf, option); | ||
| }, | ||
| inflateAsync: function (/*Function*/ callback) { | ||
| var tmp = zlib.createInflateRaw(option), | ||
| parts = [], | ||
| total = 0; | ||
| tmp.on("data", function (data) { | ||
| parts.push(data); | ||
| total += data.length; | ||
| }); | ||
| tmp.on("end", function () { | ||
| var buf = Buffer.alloc(total), | ||
| written = 0; | ||
| buf.fill(0); | ||
| for (var i = 0; i < parts.length; i++) { | ||
| var part = parts[i]; | ||
| part.copy(buf, written); | ||
| written += part.length; | ||
| } | ||
| callback && callback(buf); | ||
| }); | ||
| tmp.end(inbuf); | ||
| } | ||
| }; | ||
| }; |
| "use strict"; | ||
| // node crypt, we use it for generate salt | ||
| // eslint-disable-next-line node/no-unsupported-features/node-builtins | ||
| const { randomFillSync } = require("crypto"); | ||
| const Errors = require("../util/errors"); | ||
| // generate CRC32 lookup table | ||
| const crctable = new Uint32Array(256).map((t, crc) => { | ||
| for (let j = 0; j < 8; j++) { | ||
| if (0 !== (crc & 1)) { | ||
| crc = (crc >>> 1) ^ 0xedb88320; | ||
| } else { | ||
| crc >>>= 1; | ||
| } | ||
| } | ||
| return crc >>> 0; | ||
| }); | ||
| // C-style uInt32 Multiply (discards higher bits, when JS multiply discards lower bits) | ||
| const uMul = (a, b) => Math.imul(a, b) >>> 0; | ||
| // crc32 byte single update (actually same function is part of utils.crc32 function :) ) | ||
| const crc32update = (pCrc32, bval) => { | ||
| return crctable[(pCrc32 ^ bval) & 0xff] ^ (pCrc32 >>> 8); | ||
| }; | ||
| // function for generating salt for encrytion header | ||
| const genSalt = () => { | ||
| if ("function" === typeof randomFillSync) { | ||
| return randomFillSync(Buffer.alloc(12)); | ||
| } else { | ||
| // fallback if function is not defined | ||
| return genSalt.node(); | ||
| } | ||
| }; | ||
| // salt generation with node random function (mainly as fallback) | ||
| genSalt.node = () => { | ||
| const salt = Buffer.alloc(12); | ||
| const len = salt.length; | ||
| for (let i = 0; i < len; i++) salt[i] = (Math.random() * 256) & 0xff; | ||
| return salt; | ||
| }; | ||
| // general config | ||
| const config = { | ||
| genSalt | ||
| }; | ||
| // Class Initkeys handles same basic ops with keys | ||
| function Initkeys(pw) { | ||
| const pass = Buffer.isBuffer(pw) ? pw : Buffer.from(pw); | ||
| this.keys = new Uint32Array([0x12345678, 0x23456789, 0x34567890]); | ||
| for (let i = 0; i < pass.length; i++) { | ||
| this.updateKeys(pass[i]); | ||
| } | ||
| } | ||
| Initkeys.prototype.updateKeys = function (byteValue) { | ||
| const keys = this.keys; | ||
| keys[0] = crc32update(keys[0], byteValue); | ||
| keys[1] += keys[0] & 0xff; | ||
| keys[1] = uMul(keys[1], 134775813) + 1; | ||
| keys[2] = crc32update(keys[2], keys[1] >>> 24); | ||
| return byteValue; | ||
| }; | ||
| Initkeys.prototype.next = function () { | ||
| const k = (this.keys[2] | 2) >>> 0; // key | ||
| return (uMul(k, k ^ 1) >> 8) & 0xff; // decode | ||
| }; | ||
| function make_decrypter(/*Buffer*/ pwd) { | ||
| // 1. Stage initialize key | ||
| const keys = new Initkeys(pwd); | ||
| // return decrypter function | ||
| return function (/*Buffer*/ data) { | ||
| // result - we create new Buffer for results | ||
| const result = Buffer.alloc(data.length); | ||
| let pos = 0; | ||
| // process input data | ||
| for (let c of data) { | ||
| //c ^= keys.next(); | ||
| //result[pos++] = c; // decode & Save Value | ||
| result[pos++] = keys.updateKeys(c ^ keys.next()); // update keys with decoded byte | ||
| } | ||
| return result; | ||
| }; | ||
| } | ||
| function make_encrypter(/*Buffer*/ pwd) { | ||
| // 1. Stage initialize key | ||
| const keys = new Initkeys(pwd); | ||
| // return encrypting function, result and pos is here so we dont have to merge buffers later | ||
| return function (/*Buffer*/ data, /*Buffer*/ result, /* Number */ pos = 0) { | ||
| // result - we create new Buffer for results | ||
| if (!result) result = Buffer.alloc(data.length); | ||
| // process input data | ||
| for (let c of data) { | ||
| const k = keys.next(); // save key byte | ||
| result[pos++] = c ^ k; // save val | ||
| keys.updateKeys(c); // update keys with decoded byte | ||
| } | ||
| return result; | ||
| }; | ||
| } | ||
| function decrypt(/*Buffer*/ data, /*Object*/ header, /*String, Buffer*/ pwd) { | ||
| if (!data || !Buffer.isBuffer(data) || data.length < 12) { | ||
| return Buffer.alloc(0); | ||
| } | ||
| // 1. We Initialize and generate decrypting function | ||
| const decrypter = make_decrypter(pwd); | ||
| // 2. decrypt salt what is always 12 bytes and is a part of file content | ||
| const salt = decrypter(data.slice(0, 12)); | ||
| // if bit 3 (0x08) of the general-purpose flags field is set, check salt[11] with the high byte of the header time | ||
| // 2 byte data block (as per Info-Zip spec), otherwise check with the high byte of the header entry | ||
| const verifyByte = (header.flags & 0x8) === 0x8 ? header.timeHighByte : header.crc >>> 24; | ||
| //3. does password meet expectations | ||
| if (salt[11] !== verifyByte) { | ||
| throw Errors.WRONG_PASSWORD(); | ||
| } | ||
| // 4. decode content | ||
| return decrypter(data.slice(12)); | ||
| } | ||
| // lets add way to populate salt, NOT RECOMMENDED for production but maybe useful for testing general functionality | ||
| function _salter(data) { | ||
| if (Buffer.isBuffer(data) && data.length >= 12) { | ||
| // be aware - currently salting buffer data is modified | ||
| config.genSalt = function () { | ||
| return data.slice(0, 12); | ||
| }; | ||
| } else if (data === "node") { | ||
| // test salt generation with node random function | ||
| config.genSalt = genSalt.node; | ||
| } else { | ||
| // if value is not acceptable config gets reset. | ||
| config.genSalt = genSalt; | ||
| } | ||
| } | ||
| function encrypt(/*Buffer*/ data, /*Object*/ header, /*String, Buffer*/ pwd, /*Boolean*/ oldlike = false) { | ||
| // 1. test data if data is not Buffer we make buffer from it | ||
| if (data == null) data = Buffer.alloc(0); | ||
| // if data is not buffer be make buffer from it | ||
| if (!Buffer.isBuffer(data)) data = Buffer.from(data.toString()); | ||
| // 2. We Initialize and generate encrypting function | ||
| const encrypter = make_encrypter(pwd); | ||
| // 3. generate salt (12-bytes of random data) | ||
| const salt = config.genSalt(); | ||
| salt[11] = (header.crc >>> 24) & 0xff; | ||
| // old implementations (before PKZip 2.04g) used two byte check | ||
| if (oldlike) salt[10] = (header.crc >>> 16) & 0xff; | ||
| // 4. create output | ||
| const result = Buffer.alloc(data.length + 12); | ||
| encrypter(salt, result); | ||
| // finally encode content | ||
| return encrypter(data, result, 12); | ||
| } | ||
| module.exports = { decrypt, encrypt, _salter }; |
| { | ||
| "name": "adm-zip", | ||
| "version": "0.5.17", | ||
| "description": "Javascript implementation of zip for nodejs with support for electron original-fs. Allows user to create or extract zip files both in memory or to/from disk", | ||
| "scripts": { | ||
| "test": "mocha -R spec", | ||
| "test:format": "npm run format:prettier:raw -- --check", | ||
| "format": "npm run format:prettier", | ||
| "format:prettier": "npm run format:prettier:raw -- --write", | ||
| "format:prettier:raw": "prettier \"**/*.{js,yml,json}\"" | ||
| }, | ||
| "keywords": [ | ||
| "zip", | ||
| "methods", | ||
| "archive", | ||
| "unzip" | ||
| ], | ||
| "homepage": "https://github.com/cthackers/adm-zip", | ||
| "author": "Nasca Iacob <sy@another-d-mention.ro> (https://github.com/cthackers)", | ||
| "bugs": { | ||
| "email": "sy@another-d-mention.ro", | ||
| "url": "https://github.com/cthackers/adm-zip/issues" | ||
| }, | ||
| "license": "MIT", | ||
| "files": [ | ||
| "adm-zip.js", | ||
| "headers", | ||
| "methods", | ||
| "util", | ||
| "zipEntry.js", | ||
| "zipFile.js", | ||
| "LICENSE" | ||
| ], | ||
| "main": "adm-zip.js", | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "https://github.com/cthackers/adm-zip.git" | ||
| }, | ||
| "engines": { | ||
| "node": ">=12.0" | ||
| }, | ||
| "overrides": { | ||
| "mocha": { | ||
| "chokidar": "^4.0.3" | ||
| } | ||
| }, | ||
| "devDependencies": { | ||
| "chai": "^6.2.2", | ||
| "iconv-lite": "^0.7.2", | ||
| "mocha": "12.0.0-beta-10", | ||
| "prettier": "^3.8.1", | ||
| "rimraf": "^3.0.2" | ||
| } | ||
| } |
| # ADM-ZIP for NodeJS | ||
| ADM-ZIP is a pure JavaScript implementation for zip data compression for [NodeJS](https://nodejs.org/). | ||
| <a href="https://github.com/cthackers/adm-zip/actions/workflows/ci.yml"> | ||
| <img src="https://github.com/cthackers/adm-zip/actions/workflows/ci.yml/badge.svg" alt="Build Status"> | ||
| </a> | ||
| # Installation | ||
| With [npm](https://www.npmjs.com/) do: | ||
| $ npm install adm-zip | ||
| **Electron** file system support described below. | ||
| ## What is it good for? | ||
| The library allows you to: | ||
| - decompress zip files directly to disk or in memory buffers | ||
| - compress files and store them to disk in .zip format or in compressed buffers | ||
| - update content of/add new/delete files from an existing .zip | ||
| # Dependencies | ||
| There are no other nodeJS libraries that ADM-ZIP is dependent of | ||
| # Examples | ||
| ## Basic usage | ||
| ```javascript | ||
| var AdmZip = require("adm-zip"); | ||
| // reading archives | ||
| var zip = new AdmZip("./my_file.zip"); | ||
| var password = "1234567890"; | ||
| var zipEntries = zip.getEntries(); // an array of ZipEntry records - add password parameter if entries are password protected | ||
| zipEntries.forEach(function (zipEntry) { | ||
| console.log(zipEntry.toString()); // outputs zip entries information | ||
| if (zipEntry.entryName == "my_file.txt") { | ||
| console.log(zipEntry.getData().toString("utf8")); | ||
| } | ||
| }); | ||
| // outputs the content of some_folder/my_file.txt | ||
| console.log(zip.readAsText("some_folder/my_file.txt")); | ||
| // extracts the specified file to the specified location | ||
| zip.extractEntryTo(/*entry name*/ "some_folder/my_file.txt", /*target path*/ "/home/me/tempfolder", /*maintainEntryPath*/ false, /*overwrite*/ true); | ||
| // extracts everything | ||
| zip.extractAllTo(/*target path*/ "/home/me/zipcontent/", /*overwrite*/ true); | ||
| // creating archives | ||
| var zip = new AdmZip(); | ||
| // add file directly | ||
| var content = "inner content of the file"; | ||
| zip.addFile("test.txt", Buffer.from(content, "utf8"), "entry comment goes here"); | ||
| // add local file | ||
| zip.addLocalFile("/home/me/some_picture.png"); | ||
| // get everything as a buffer | ||
| var willSendthis = zip.toBuffer(); | ||
| // or write everything to disk | ||
| zip.writeZip(/*target file name*/ "/home/me/files.zip"); | ||
| // ... more examples in the wiki | ||
| ``` | ||
| For more detailed information please check out the [wiki](https://github.com/cthackers/adm-zip/wiki). | ||
| ## Electron original-fs | ||
| ADM-ZIP has supported electron **original-fs** for years without any user interractions but it causes problem with bundlers like rollup etc. For continuing support **original-fs** or any other custom file system module. There is possible specify your module by **fs** option in ADM-ZIP constructor. | ||
| Example: | ||
| ```javascript | ||
| const AdmZip = require("adm-zip"); | ||
| const OriginalFs = require("original-fs"); | ||
| // reading archives | ||
| const zip = new AdmZip("./my_file.zip", { fs: OriginalFs }); | ||
| . | ||
| . | ||
| . | ||
| ``` |
| module.exports = { | ||
| /* The local file header */ | ||
| LOCHDR : 30, // LOC header size | ||
| LOCSIG : 0x04034b50, // "PK\003\004" | ||
| LOCVER : 4, // version needed to extract | ||
| LOCFLG : 6, // general purpose bit flag | ||
| LOCHOW : 8, // compression method | ||
| LOCTIM : 10, // modification time (2 bytes time, 2 bytes date) | ||
| LOCCRC : 14, // uncompressed file crc-32 value | ||
| LOCSIZ : 18, // compressed size | ||
| LOCLEN : 22, // uncompressed size | ||
| LOCNAM : 26, // filename length | ||
| LOCEXT : 28, // extra field length | ||
| /* The Data descriptor */ | ||
| EXTSIG : 0x08074b50, // "PK\007\008" | ||
| EXTHDR : 16, // EXT header size | ||
| EXTCRC : 4, // uncompressed file crc-32 value | ||
| EXTSIZ : 8, // compressed size | ||
| EXTLEN : 12, // uncompressed size | ||
| /* The central directory file header */ | ||
| CENHDR : 46, // CEN header size | ||
| CENSIG : 0x02014b50, // "PK\001\002" | ||
| CENVEM : 4, // version made by | ||
| CENVER : 6, // version needed to extract | ||
| CENFLG : 8, // encrypt, decrypt flags | ||
| CENHOW : 10, // compression method | ||
| CENTIM : 12, // modification time (2 bytes time, 2 bytes date) | ||
| CENCRC : 16, // uncompressed file crc-32 value | ||
| CENSIZ : 20, // compressed size | ||
| CENLEN : 24, // uncompressed size | ||
| CENNAM : 28, // filename length | ||
| CENEXT : 30, // extra field length | ||
| CENCOM : 32, // file comment length | ||
| CENDSK : 34, // volume number start | ||
| CENATT : 36, // internal file attributes | ||
| CENATX : 38, // external file attributes (host system dependent) | ||
| CENOFF : 42, // LOC header offset | ||
| /* The entries in the end of central directory */ | ||
| ENDHDR : 22, // END header size | ||
| ENDSIG : 0x06054b50, // "PK\005\006" | ||
| ENDSUB : 8, // number of entries on this disk | ||
| ENDTOT : 10, // total number of entries | ||
| ENDSIZ : 12, // central directory size in bytes | ||
| ENDOFF : 16, // offset of first CEN header | ||
| ENDCOM : 20, // zip file comment length | ||
| END64HDR : 20, // zip64 END header size | ||
| END64SIG : 0x07064b50, // zip64 Locator signature, "PK\006\007" | ||
| END64START : 4, // number of the disk with the start of the zip64 | ||
| END64OFF : 8, // relative offset of the zip64 end of central directory | ||
| END64NUMDISKS : 16, // total number of disks | ||
| ZIP64SIG : 0x06064b50, // zip64 signature, "PK\006\006" | ||
| ZIP64HDR : 56, // zip64 record minimum size | ||
| ZIP64LEAD : 12, // leading bytes at the start of the record, not counted by the value stored in ZIP64SIZE | ||
| ZIP64SIZE : 4, // zip64 size of the central directory record | ||
| ZIP64VEM : 12, // zip64 version made by | ||
| ZIP64VER : 14, // zip64 version needed to extract | ||
| ZIP64DSK : 16, // zip64 number of this disk | ||
| ZIP64DSKDIR : 20, // number of the disk with the start of the record directory | ||
| ZIP64SUB : 24, // number of entries on this disk | ||
| ZIP64TOT : 32, // total number of entries | ||
| ZIP64SIZB : 40, // zip64 central directory size in bytes | ||
| ZIP64OFF : 48, // offset of start of central directory with respect to the starting disk number | ||
| ZIP64EXTRA : 56, // extensible data sector | ||
| /* Compression methods */ | ||
| STORED : 0, // no compression | ||
| SHRUNK : 1, // shrunk | ||
| REDUCED1 : 2, // reduced with compression factor 1 | ||
| REDUCED2 : 3, // reduced with compression factor 2 | ||
| REDUCED3 : 4, // reduced with compression factor 3 | ||
| REDUCED4 : 5, // reduced with compression factor 4 | ||
| IMPLODED : 6, // imploded | ||
| // 7 reserved for Tokenizing compression algorithm | ||
| DEFLATED : 8, // deflated | ||
| ENHANCED_DEFLATED: 9, // enhanced deflated | ||
| PKWARE : 10,// PKWare DCL imploded | ||
| // 11 reserved by PKWARE | ||
| BZIP2 : 12, // compressed using BZIP2 | ||
| // 13 reserved by PKWARE | ||
| LZMA : 14, // LZMA | ||
| // 15-17 reserved by PKWARE | ||
| IBM_TERSE : 18, // compressed using IBM TERSE | ||
| IBM_LZ77 : 19, // IBM LZ77 z | ||
| AES_ENCRYPT : 99, // WinZIP AES encryption method | ||
| /* General purpose bit flag */ | ||
| // values can obtained with expression 2**bitnr | ||
| FLG_ENC : 1, // Bit 0: encrypted file | ||
| FLG_COMP1 : 2, // Bit 1, compression option | ||
| FLG_COMP2 : 4, // Bit 2, compression option | ||
| FLG_DESC : 8, // Bit 3, data descriptor | ||
| FLG_ENH : 16, // Bit 4, enhanced deflating | ||
| FLG_PATCH : 32, // Bit 5, indicates that the file is compressed patched data. | ||
| FLG_STR : 64, // Bit 6, strong encryption (patented) | ||
| // Bits 7-10: Currently unused. | ||
| FLG_EFS : 2048, // Bit 11: Language encoding flag (EFS) | ||
| // Bit 12: Reserved by PKWARE for enhanced compression. | ||
| // Bit 13: encrypted the Central Directory (patented). | ||
| // Bits 14-15: Reserved by PKWARE. | ||
| FLG_MSK : 4096, // mask header values | ||
| /* Load type */ | ||
| FILE : 2, | ||
| BUFFER : 1, | ||
| NONE : 0, | ||
| /* 4.5 Extensible data fields */ | ||
| EF_ID : 0, | ||
| EF_SIZE : 2, | ||
| /* Header IDs */ | ||
| ID_ZIP64 : 0x0001, | ||
| ID_AVINFO : 0x0007, | ||
| ID_PFS : 0x0008, | ||
| ID_OS2 : 0x0009, | ||
| ID_NTFS : 0x000a, | ||
| ID_OPENVMS : 0x000c, | ||
| ID_UNIX : 0x000d, | ||
| ID_FORK : 0x000e, | ||
| ID_PATCH : 0x000f, | ||
| ID_X509_PKCS7 : 0x0014, | ||
| ID_X509_CERTID_F : 0x0015, | ||
| ID_X509_CERTID_C : 0x0016, | ||
| ID_STRONGENC : 0x0017, | ||
| ID_RECORD_MGT : 0x0018, | ||
| ID_X509_PKCS7_RL : 0x0019, | ||
| ID_IBM1 : 0x0065, | ||
| ID_IBM2 : 0x0066, | ||
| ID_POSZIP : 0x4690, | ||
| EF_ZIP64_OR_32 : 0xffffffff, | ||
| EF_ZIP64_OR_16 : 0xffff, | ||
| EF_ZIP64_SUNCOMP : 0, | ||
| EF_ZIP64_SCOMP : 8, | ||
| EF_ZIP64_RHO : 16, | ||
| EF_ZIP64_DSN : 24 | ||
| }; |
| module.exports = { | ||
| efs: true, | ||
| encode: (data) => Buffer.from(data, "utf8"), | ||
| decode: (data) => data.toString("utf8") | ||
| }; |
| const errors = { | ||
| /* Header error messages */ | ||
| INVALID_LOC: "Invalid LOC header (bad signature)", | ||
| INVALID_CEN: "Invalid CEN header (bad signature)", | ||
| INVALID_END: "Invalid END header (bad signature)", | ||
| /* Descriptor */ | ||
| DESCRIPTOR_NOT_EXIST: "No descriptor present", | ||
| DESCRIPTOR_UNKNOWN: "Unknown descriptor format", | ||
| DESCRIPTOR_FAULTY: "Descriptor data is malformed", | ||
| /* ZipEntry error messages*/ | ||
| NO_DATA: "Nothing to decompress", | ||
| BAD_CRC: "CRC32 checksum failed {0}", | ||
| FILE_IN_THE_WAY: "There is a file in the way: {0}", | ||
| UNKNOWN_METHOD: "Invalid/unsupported compression method", | ||
| /* Inflater error messages */ | ||
| AVAIL_DATA: "inflate::Available inflate data did not terminate", | ||
| INVALID_DISTANCE: "inflate::Invalid literal/length or distance code in fixed or dynamic block", | ||
| TO_MANY_CODES: "inflate::Dynamic block code description: too many length or distance codes", | ||
| INVALID_REPEAT_LEN: "inflate::Dynamic block code description: repeat more than specified lengths", | ||
| INVALID_REPEAT_FIRST: "inflate::Dynamic block code description: repeat lengths with no first length", | ||
| INCOMPLETE_CODES: "inflate::Dynamic block code description: code lengths codes incomplete", | ||
| INVALID_DYN_DISTANCE: "inflate::Dynamic block code description: invalid distance code lengths", | ||
| INVALID_CODES_LEN: "inflate::Dynamic block code description: invalid literal/length code lengths", | ||
| INVALID_STORE_BLOCK: "inflate::Stored block length did not match one's complement", | ||
| INVALID_BLOCK_TYPE: "inflate::Invalid block type (type == 3)", | ||
| /* ADM-ZIP error messages */ | ||
| CANT_EXTRACT_FILE: "Could not extract the file", | ||
| CANT_OVERRIDE: "Target file already exists", | ||
| DISK_ENTRY_TOO_LARGE: "Number of disk entries is too large", | ||
| NO_ZIP: "No zip file was loaded", | ||
| NO_ENTRY: "Entry doesn't exist", | ||
| DIRECTORY_CONTENT_ERROR: "A directory cannot have content", | ||
| FILE_NOT_FOUND: 'File not found: "{0}"', | ||
| NOT_IMPLEMENTED: "Not implemented", | ||
| INVALID_FILENAME: "Invalid filename", | ||
| INVALID_FORMAT: "Invalid or unsupported zip format. No END header found", | ||
| INVALID_PASS_PARAM: "Incompatible password parameter", | ||
| WRONG_PASSWORD: "Wrong Password", | ||
| /* ADM-ZIP */ | ||
| COMMENT_TOO_LONG: "Comment is too long", // Comment can be max 65535 bytes long (NOTE: some non-US characters may take more space) | ||
| EXTRA_FIELD_PARSE_ERROR: "Extra field parsing error" | ||
| }; | ||
| // template | ||
| function E(message) { | ||
| return function (...args) { | ||
| if (args.length) { // Allow {0} .. {9} arguments in error message, based on argument number | ||
| message = message.replace(/\{(\d)\}/g, (_, n) => args[n] || ''); | ||
| } | ||
| return new Error('ADM-ZIP: ' + message); | ||
| }; | ||
| } | ||
| // Init errors with template | ||
| for (const msg of Object.keys(errors)) { | ||
| exports[msg] = E(errors[msg]); | ||
| } |
| const pth = require("path"); | ||
| module.exports = function (/*String*/ path, /*Utils object*/ { fs }) { | ||
| var _path = path || "", | ||
| _obj = newAttr(), | ||
| _stat = null; | ||
| function newAttr() { | ||
| return { | ||
| directory: false, | ||
| readonly: false, | ||
| hidden: false, | ||
| executable: false, | ||
| mtime: 0, | ||
| atime: 0 | ||
| }; | ||
| } | ||
| if (_path && fs.existsSync(_path)) { | ||
| _stat = fs.statSync(_path); | ||
| _obj.directory = _stat.isDirectory(); | ||
| _obj.mtime = _stat.mtime; | ||
| _obj.atime = _stat.atime; | ||
| _obj.executable = (0o111 & _stat.mode) !== 0; // file is executable who ever har right not just owner | ||
| _obj.readonly = (0o200 & _stat.mode) === 0; // readonly if owner has no write right | ||
| _obj.hidden = pth.basename(_path)[0] === "."; | ||
| } else { | ||
| console.warn("Invalid path: " + _path); | ||
| } | ||
| return { | ||
| get directory() { | ||
| return _obj.directory; | ||
| }, | ||
| get readOnly() { | ||
| return _obj.readonly; | ||
| }, | ||
| get hidden() { | ||
| return _obj.hidden; | ||
| }, | ||
| get mtime() { | ||
| return _obj.mtime; | ||
| }, | ||
| get atime() { | ||
| return _obj.atime; | ||
| }, | ||
| get executable() { | ||
| return _obj.executable; | ||
| }, | ||
| decodeAttributes: function () {}, | ||
| encodeAttributes: function () {}, | ||
| toJSON: function () { | ||
| return { | ||
| path: _path, | ||
| isDirectory: _obj.directory, | ||
| isReadOnly: _obj.readonly, | ||
| isHidden: _obj.hidden, | ||
| isExecutable: _obj.executable, | ||
| mTime: _obj.mtime, | ||
| aTime: _obj.atime | ||
| }; | ||
| }, | ||
| toString: function () { | ||
| return JSON.stringify(this.toJSON(), null, "\t"); | ||
| } | ||
| }; | ||
| }; |
| module.exports = require("./utils"); | ||
| module.exports.Constants = require("./constants"); | ||
| module.exports.Errors = require("./errors"); | ||
| module.exports.FileAttr = require("./fattr"); | ||
| module.exports.decoder = require("./decoder"); |
| const fsystem = require("fs"); | ||
| const pth = require("path"); | ||
| const Constants = require("./constants"); | ||
| const Errors = require("./errors"); | ||
| const isWin = typeof process === "object" && "win32" === process.platform; | ||
| const is_Obj = (obj) => typeof obj === "object" && obj !== null; | ||
| // generate CRC32 lookup table | ||
| const crcTable = new Uint32Array(256).map((t, c) => { | ||
| for (let k = 0; k < 8; k++) { | ||
| if ((c & 1) !== 0) { | ||
| c = 0xedb88320 ^ (c >>> 1); | ||
| } else { | ||
| c >>>= 1; | ||
| } | ||
| } | ||
| return c >>> 0; | ||
| }); | ||
| // UTILS functions | ||
| function Utils(opts) { | ||
| this.sep = pth.sep; | ||
| this.fs = fsystem; | ||
| if (is_Obj(opts)) { | ||
| // custom filesystem | ||
| if (is_Obj(opts.fs) && typeof opts.fs.statSync === "function") { | ||
| this.fs = opts.fs; | ||
| } | ||
| } | ||
| } | ||
| module.exports = Utils; | ||
| // INSTANTIABLE functions | ||
| Utils.prototype.makeDir = function (/*String*/ folder) { | ||
| const self = this; | ||
| // Sync - make directories tree | ||
| function mkdirSync(/*String*/ fpath) { | ||
| let resolvedPath = fpath.split(self.sep)[0]; | ||
| fpath.split(self.sep).forEach(function (name) { | ||
| if (!name || name.substr(-1, 1) === ":") return; | ||
| resolvedPath += self.sep + name; | ||
| var stat; | ||
| try { | ||
| stat = self.fs.statSync(resolvedPath); | ||
| } catch (e) { | ||
| if (e.message && e.message.startsWith('ENOENT')) { | ||
| self.fs.mkdirSync(resolvedPath); | ||
| } else { | ||
| throw e; | ||
| } | ||
| } | ||
| if (stat && stat.isFile()) throw Errors.FILE_IN_THE_WAY(`"${resolvedPath}"`); | ||
| }); | ||
| } | ||
| mkdirSync(folder); | ||
| }; | ||
| Utils.prototype.writeFileTo = function (/*String*/ path, /*Buffer*/ content, /*Boolean*/ overwrite, /*Number*/ attr) { | ||
| const self = this; | ||
| if (self.fs.existsSync(path)) { | ||
| if (!overwrite) return false; // cannot overwrite | ||
| var stat = self.fs.statSync(path); | ||
| if (stat.isDirectory()) { | ||
| return false; | ||
| } | ||
| } | ||
| var folder = pth.dirname(path); | ||
| if (!self.fs.existsSync(folder)) { | ||
| self.makeDir(folder); | ||
| } | ||
| var fd; | ||
| try { | ||
| fd = self.fs.openSync(path, "w", 0o666); // 0666 | ||
| } catch (e) { | ||
| self.fs.chmodSync(path, 0o666); | ||
| fd = self.fs.openSync(path, "w", 0o666); | ||
| } | ||
| if (fd) { | ||
| try { | ||
| self.fs.writeSync(fd, content, 0, content.length, 0); | ||
| } finally { | ||
| self.fs.closeSync(fd); | ||
| } | ||
| } | ||
| self.fs.chmodSync(path, attr || 0o666); | ||
| return true; | ||
| }; | ||
| Utils.prototype.writeFileToAsync = function (/*String*/ path, /*Buffer*/ content, /*Boolean*/ overwrite, /*Number*/ attr, /*Function*/ callback) { | ||
| if (typeof attr === "function") { | ||
| callback = attr; | ||
| attr = undefined; | ||
| } | ||
| const self = this; | ||
| self.fs.exists(path, function (exist) { | ||
| if (exist && !overwrite) return callback(false); | ||
| self.fs.stat(path, function (err, stat) { | ||
| if (exist && stat.isDirectory()) { | ||
| return callback(false); | ||
| } | ||
| var folder = pth.dirname(path); | ||
| self.fs.exists(folder, function (exists) { | ||
| if (!exists) self.makeDir(folder); | ||
| self.fs.open(path, "w", 0o666, function (err, fd) { | ||
| if (err) { | ||
| self.fs.chmod(path, 0o666, function () { | ||
| self.fs.open(path, "w", 0o666, function (err, fd) { | ||
| self.fs.write(fd, content, 0, content.length, 0, function () { | ||
| self.fs.close(fd, function () { | ||
| self.fs.chmod(path, attr || 0o666, function () { | ||
| callback(true); | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
| } else if (fd) { | ||
| self.fs.write(fd, content, 0, content.length, 0, function () { | ||
| self.fs.close(fd, function () { | ||
| self.fs.chmod(path, attr || 0o666, function () { | ||
| callback(true); | ||
| }); | ||
| }); | ||
| }); | ||
| } else { | ||
| self.fs.chmod(path, attr || 0o666, function () { | ||
| callback(true); | ||
| }); | ||
| } | ||
| }); | ||
| }); | ||
| }); | ||
| }); | ||
| }; | ||
| Utils.prototype.findFiles = function (/*String*/ path) { | ||
| const self = this; | ||
| function findSync(/*String*/ dir, /*RegExp*/ pattern, /*Boolean*/ recursive) { | ||
| if (typeof pattern === "boolean") { | ||
| recursive = pattern; | ||
| pattern = undefined; | ||
| } | ||
| let files = []; | ||
| self.fs.readdirSync(dir).forEach(function (file) { | ||
| const path = pth.join(dir, file); | ||
| const stat = self.fs.statSync(path); | ||
| if (!pattern || pattern.test(path)) { | ||
| files.push(pth.normalize(path) + (stat.isDirectory() ? self.sep : "")); | ||
| } | ||
| if (stat.isDirectory() && recursive) files = files.concat(findSync(path, pattern, recursive)); | ||
| }); | ||
| return files; | ||
| } | ||
| return findSync(path, undefined, true); | ||
| }; | ||
| /** | ||
| * Callback for showing if everything was done. | ||
| * | ||
| * @callback filelistCallback | ||
| * @param {Error} err - Error object | ||
| * @param {string[]} list - was request fully completed | ||
| */ | ||
| /** | ||
| * | ||
| * @param {string} dir | ||
| * @param {filelistCallback} cb | ||
| */ | ||
| Utils.prototype.findFilesAsync = function (dir, cb) { | ||
| const self = this; | ||
| let results = []; | ||
| self.fs.readdir(dir, function (err, list) { | ||
| if (err) return cb(err); | ||
| let list_length = list.length; | ||
| if (!list_length) return cb(null, results); | ||
| list.forEach(function (file) { | ||
| file = pth.join(dir, file); | ||
| self.fs.stat(file, function (err, stat) { | ||
| if (err) return cb(err); | ||
| if (stat) { | ||
| results.push(pth.normalize(file) + (stat.isDirectory() ? self.sep : "")); | ||
| if (stat.isDirectory()) { | ||
| self.findFilesAsync(file, function (err, res) { | ||
| if (err) return cb(err); | ||
| results = results.concat(res); | ||
| if (!--list_length) cb(null, results); | ||
| }); | ||
| } else { | ||
| if (!--list_length) cb(null, results); | ||
| } | ||
| } | ||
| }); | ||
| }); | ||
| }); | ||
| }; | ||
| Utils.prototype.getAttributes = function () {}; | ||
| Utils.prototype.setAttributes = function () {}; | ||
| // STATIC functions | ||
| // crc32 single update (it is part of crc32) | ||
| Utils.crc32update = function (crc, byte) { | ||
| return crcTable[(crc ^ byte) & 0xff] ^ (crc >>> 8); | ||
| }; | ||
| Utils.crc32 = function (buf) { | ||
| if (typeof buf === "string") { | ||
| buf = Buffer.from(buf, "utf8"); | ||
| } | ||
| let len = buf.length; | ||
| let crc = ~0; | ||
| for (let off = 0; off < len; ) crc = Utils.crc32update(crc, buf[off++]); | ||
| // xor and cast as uint32 number | ||
| return ~crc >>> 0; | ||
| }; | ||
| Utils.methodToString = function (/*Number*/ method) { | ||
| switch (method) { | ||
| case Constants.STORED: | ||
| return "STORED (" + method + ")"; | ||
| case Constants.DEFLATED: | ||
| return "DEFLATED (" + method + ")"; | ||
| default: | ||
| return "UNSUPPORTED (" + method + ")"; | ||
| } | ||
| }; | ||
| /** | ||
| * removes ".." style path elements | ||
| * @param {string} path - fixable path | ||
| * @returns string - fixed filepath | ||
| */ | ||
| Utils.canonical = function (/*string*/ path) { | ||
| if (!path) return ""; | ||
| // trick normalize think path is absolute | ||
| const safeSuffix = pth.posix.normalize("/" + path.split("\\").join("/")); | ||
| return pth.join(".", safeSuffix); | ||
| }; | ||
| /** | ||
| * fix file names in achive | ||
| * @param {string} path - fixable path | ||
| * @returns string - fixed filepath | ||
| */ | ||
| Utils.zipnamefix = function (path) { | ||
| if (!path) return ""; | ||
| // trick normalize think path is absolute | ||
| const safeSuffix = pth.posix.normalize("/" + path.split("\\").join("/")); | ||
| return pth.posix.join(".", safeSuffix); | ||
| }; | ||
| /** | ||
| * | ||
| * @param {Array} arr | ||
| * @param {function} callback | ||
| * @returns | ||
| */ | ||
| Utils.findLast = function (arr, callback) { | ||
| if (!Array.isArray(arr)) throw new TypeError("arr is not array"); | ||
| const len = arr.length >>> 0; | ||
| for (let i = len - 1; i >= 0; i--) { | ||
| if (callback(arr[i], i, arr)) { | ||
| return arr[i]; | ||
| } | ||
| } | ||
| return void 0; | ||
| }; | ||
| // make abolute paths taking prefix as root folder | ||
| Utils.sanitize = function (/*string*/ prefix, /*string*/ name) { | ||
| prefix = pth.resolve(pth.normalize(prefix)); | ||
| var parts = name.split("/"); | ||
| for (var i = 0, l = parts.length; i < l; i++) { | ||
| var path = pth.normalize(pth.join(prefix, parts.slice(i, l).join(pth.sep))); | ||
| if (path.indexOf(prefix) === 0) { | ||
| return path; | ||
| } | ||
| } | ||
| return pth.normalize(pth.join(prefix, pth.basename(name))); | ||
| }; | ||
| // converts buffer, Uint8Array, string types to buffer | ||
| Utils.toBuffer = function toBuffer(/*buffer, Uint8Array, string*/ input, /* function */ encoder) { | ||
| if (Buffer.isBuffer(input)) { | ||
| return input; | ||
| } else if (input instanceof Uint8Array) { | ||
| return Buffer.from(input); | ||
| } else { | ||
| // expect string all other values are invalid and return empty buffer | ||
| return typeof input === "string" ? encoder(input) : Buffer.alloc(0); | ||
| } | ||
| }; | ||
| Utils.readBigUInt64LE = function (/*Buffer*/ buffer, /*int*/ index) { | ||
| const lo = buffer.readUInt32LE(index); | ||
| const hi = buffer.readUInt32LE(index + 4); | ||
| return hi * 0x100000000 + lo; | ||
| }; | ||
| Utils.fromDOS2Date = function (val) { | ||
| return new Date(((val >> 25) & 0x7f) + 1980, Math.max(((val >> 21) & 0x0f) - 1, 0), Math.max((val >> 16) & 0x1f, 1), (val >> 11) & 0x1f, (val >> 5) & 0x3f, (val & 0x1f) << 1); | ||
| }; | ||
| Utils.fromDate2DOS = function (val) { | ||
| let date = 0; | ||
| let time = 0; | ||
| if (val.getFullYear() > 1979) { | ||
| date = (((val.getFullYear() - 1980) & 0x7f) << 9) | ((val.getMonth() + 1) << 5) | val.getDate(); | ||
| time = (val.getHours() << 11) | (val.getMinutes() << 5) | (val.getSeconds() >> 1); | ||
| } | ||
| return (date << 16) | time; | ||
| }; | ||
| Utils.isWin = isWin; // Do we have windows system | ||
| Utils.crcTable = crcTable; |
| var Utils = require("./util"), | ||
| Headers = require("./headers"), | ||
| Constants = Utils.Constants, | ||
| Methods = require("./methods"); | ||
| module.exports = function (/** object */ options, /*Buffer*/ input) { | ||
| var _centralHeader = new Headers.EntryHeader(), | ||
| _entryName = Buffer.alloc(0), | ||
| _comment = Buffer.alloc(0), | ||
| _isDirectory = false, | ||
| uncompressedData = null, | ||
| _extra = Buffer.alloc(0), | ||
| _extralocal = Buffer.alloc(0), | ||
| _efs = true; | ||
| // assign options | ||
| const opts = options; | ||
| const decoder = typeof opts.decoder === "object" ? opts.decoder : Utils.decoder; | ||
| _efs = decoder.hasOwnProperty("efs") ? decoder.efs : false; | ||
| function getCompressedDataFromZip() { | ||
| //if (!input || !Buffer.isBuffer(input)) { | ||
| if (!input || !(input instanceof Uint8Array)) { | ||
| return Buffer.alloc(0); | ||
| } | ||
| _extralocal = _centralHeader.loadLocalHeaderFromBinary(input); | ||
| return input.slice(_centralHeader.realDataOffset, _centralHeader.realDataOffset + _centralHeader.compressedSize); | ||
| } | ||
| function crc32OK(data) { | ||
| // if bit 3 (0x08) of the general-purpose flags field is set, then the CRC-32 and file sizes are not known when the local header is written | ||
| if (!_centralHeader.flags_desc && !_centralHeader.localHeader.flags_desc) { | ||
| if (Utils.crc32(data) !== _centralHeader.localHeader.crc) { | ||
| return false; | ||
| } | ||
| } else { | ||
| const descriptor = {}; | ||
| const dataEndOffset = _centralHeader.realDataOffset + _centralHeader.compressedSize; | ||
| // no descriptor after compressed data, instead new local header | ||
| if (input.readUInt32LE(dataEndOffset) == Constants.LOCSIG || input.readUInt32LE(dataEndOffset) == Constants.CENSIG) { | ||
| throw Utils.Errors.DESCRIPTOR_NOT_EXIST(); | ||
| } | ||
| // get decriptor data | ||
| if (input.readUInt32LE(dataEndOffset) == Constants.EXTSIG) { | ||
| // descriptor with signature | ||
| descriptor.crc = input.readUInt32LE(dataEndOffset + Constants.EXTCRC); | ||
| descriptor.compressedSize = input.readUInt32LE(dataEndOffset + Constants.EXTSIZ); | ||
| descriptor.size = input.readUInt32LE(dataEndOffset + Constants.EXTLEN); | ||
| } else if (input.readUInt16LE(dataEndOffset + 12) === 0x4b50) { | ||
| // descriptor without signature (we check is new header starting where we expect) | ||
| descriptor.crc = input.readUInt32LE(dataEndOffset + Constants.EXTCRC - 4); | ||
| descriptor.compressedSize = input.readUInt32LE(dataEndOffset + Constants.EXTSIZ - 4); | ||
| descriptor.size = input.readUInt32LE(dataEndOffset + Constants.EXTLEN - 4); | ||
| } else { | ||
| throw Utils.Errors.DESCRIPTOR_UNKNOWN(); | ||
| } | ||
| // check data integrity | ||
| if (descriptor.compressedSize !== _centralHeader.compressedSize || descriptor.size !== _centralHeader.size || descriptor.crc !== _centralHeader.crc) { | ||
| throw Utils.Errors.DESCRIPTOR_FAULTY(); | ||
| } | ||
| if (Utils.crc32(data) !== descriptor.crc) { | ||
| return false; | ||
| } | ||
| // @TODO: zip64 bit descriptor fields | ||
| // if bit 3 is set and any value in local header "zip64 Extended information" extra field are set 0 (place holder) | ||
| // then 64-bit descriptor format is used instead of 32-bit | ||
| // central header - "zip64 Extended information" extra field should store real values and not place holders | ||
| } | ||
| return true; | ||
| } | ||
| function decompress(/*Boolean*/ async, /*Function*/ callback, /*String, Buffer*/ pass) { | ||
| if (typeof callback === "undefined" && typeof async === "string") { | ||
| pass = async; | ||
| async = void 0; | ||
| } | ||
| if (_isDirectory) { | ||
| if (async && callback) { | ||
| callback(Buffer.alloc(0), Utils.Errors.DIRECTORY_CONTENT_ERROR()); //si added error. | ||
| } | ||
| return Buffer.alloc(0); | ||
| } | ||
| var compressedData = getCompressedDataFromZip(); | ||
| if (compressedData.length === 0) { | ||
| // File is empty, nothing to decompress. | ||
| if (async && callback) callback(compressedData); | ||
| return compressedData; | ||
| } | ||
| if (_centralHeader.encrypted) { | ||
| if ("string" !== typeof pass && !Buffer.isBuffer(pass)) { | ||
| throw Utils.Errors.INVALID_PASS_PARAM(); | ||
| } | ||
| compressedData = Methods.ZipCrypto.decrypt(compressedData, _centralHeader, pass); | ||
| } | ||
| var data = Buffer.alloc(_centralHeader.size); | ||
| switch (_centralHeader.method) { | ||
| case Utils.Constants.STORED: | ||
| compressedData.copy(data); | ||
| if (!crc32OK(data)) { | ||
| if (async && callback) callback(data, Utils.Errors.BAD_CRC()); //si added error | ||
| throw Utils.Errors.BAD_CRC(); | ||
| } else { | ||
| //si added otherwise did not seem to return data. | ||
| if (async && callback) callback(data); | ||
| return data; | ||
| } | ||
| case Utils.Constants.DEFLATED: | ||
| var inflater = new Methods.Inflater(compressedData, _centralHeader.size); | ||
| if (!async) { | ||
| const result = inflater.inflate(data); | ||
| result.copy(data, 0); | ||
| if (!crc32OK(data)) { | ||
| throw Utils.Errors.BAD_CRC(`"${decoder.decode(_entryName)}"`); | ||
| } | ||
| return data; | ||
| } else { | ||
| inflater.inflateAsync(function (result) { | ||
| result.copy(result, 0); | ||
| if (callback) { | ||
| if (!crc32OK(result)) { | ||
| callback(result, Utils.Errors.BAD_CRC()); //si added error | ||
| } else { | ||
| callback(result); | ||
| } | ||
| } | ||
| }); | ||
| } | ||
| break; | ||
| default: | ||
| if (async && callback) callback(Buffer.alloc(0), Utils.Errors.UNKNOWN_METHOD()); | ||
| throw Utils.Errors.UNKNOWN_METHOD(); | ||
| } | ||
| } | ||
| function compress(/*Boolean*/ async, /*Function*/ callback) { | ||
| if ((!uncompressedData || !uncompressedData.length) && Buffer.isBuffer(input)) { | ||
| // no data set or the data wasn't changed to require recompression | ||
| if (async && callback) callback(getCompressedDataFromZip()); | ||
| return getCompressedDataFromZip(); | ||
| } | ||
| if (uncompressedData.length && !_isDirectory) { | ||
| var compressedData; | ||
| // Local file header | ||
| switch (_centralHeader.method) { | ||
| case Utils.Constants.STORED: | ||
| _centralHeader.compressedSize = _centralHeader.size; | ||
| compressedData = Buffer.alloc(uncompressedData.length); | ||
| uncompressedData.copy(compressedData); | ||
| if (async && callback) callback(compressedData); | ||
| return compressedData; | ||
| default: | ||
| case Utils.Constants.DEFLATED: | ||
| var deflater = new Methods.Deflater(uncompressedData); | ||
| if (!async) { | ||
| var deflated = deflater.deflate(); | ||
| _centralHeader.compressedSize = deflated.length; | ||
| return deflated; | ||
| } else { | ||
| deflater.deflateAsync(function (data) { | ||
| compressedData = Buffer.alloc(data.length); | ||
| _centralHeader.compressedSize = data.length; | ||
| data.copy(compressedData); | ||
| callback && callback(compressedData); | ||
| }); | ||
| } | ||
| deflater = null; | ||
| break; | ||
| } | ||
| } else if (async && callback) { | ||
| callback(Buffer.alloc(0)); | ||
| } else { | ||
| return Buffer.alloc(0); | ||
| } | ||
| } | ||
| function readUInt64LE(buffer, offset) { | ||
| return Utils.readBigUInt64LE(buffer, offset); | ||
| } | ||
| function parseExtra(data) { | ||
| try { | ||
| var offset = 0; | ||
| var signature, size, part; | ||
| while (offset + 4 < data.length) { | ||
| signature = data.readUInt16LE(offset); | ||
| offset += 2; | ||
| size = data.readUInt16LE(offset); | ||
| offset += 2; | ||
| part = data.slice(offset, offset + size); | ||
| offset += size; | ||
| if (Constants.ID_ZIP64 === signature) { | ||
| parseZip64ExtendedInformation(part); | ||
| } | ||
| } | ||
| } catch (error) { | ||
| throw Utils.Errors.EXTRA_FIELD_PARSE_ERROR(); | ||
| } | ||
| } | ||
| //Override header field values with values from the ZIP64 extra field | ||
| function parseZip64ExtendedInformation(data) { | ||
| var size, compressedSize, offset, diskNumStart; | ||
| if (data.length >= Constants.EF_ZIP64_SCOMP) { | ||
| size = readUInt64LE(data, Constants.EF_ZIP64_SUNCOMP); | ||
| if (_centralHeader.size === Constants.EF_ZIP64_OR_32) { | ||
| _centralHeader.size = size; | ||
| } | ||
| } | ||
| if (data.length >= Constants.EF_ZIP64_RHO) { | ||
| compressedSize = readUInt64LE(data, Constants.EF_ZIP64_SCOMP); | ||
| if (_centralHeader.compressedSize === Constants.EF_ZIP64_OR_32) { | ||
| _centralHeader.compressedSize = compressedSize; | ||
| } | ||
| } | ||
| if (data.length >= Constants.EF_ZIP64_DSN) { | ||
| offset = readUInt64LE(data, Constants.EF_ZIP64_RHO); | ||
| if (_centralHeader.offset === Constants.EF_ZIP64_OR_32) { | ||
| _centralHeader.offset = offset; | ||
| } | ||
| } | ||
| if (data.length >= Constants.EF_ZIP64_DSN + 4) { | ||
| diskNumStart = data.readUInt32LE(Constants.EF_ZIP64_DSN); | ||
| if (_centralHeader.diskNumStart === Constants.EF_ZIP64_OR_16) { | ||
| _centralHeader.diskNumStart = diskNumStart; | ||
| } | ||
| } | ||
| } | ||
| return { | ||
| get entryName() { | ||
| return decoder.decode(_entryName); | ||
| }, | ||
| get rawEntryName() { | ||
| return _entryName; | ||
| }, | ||
| set entryName(val) { | ||
| _entryName = Utils.toBuffer(val, decoder.encode); | ||
| var lastChar = _entryName[_entryName.length - 1]; | ||
| _isDirectory = lastChar === 47 || lastChar === 92; | ||
| _centralHeader.fileNameLength = _entryName.length; | ||
| }, | ||
| get efs() { | ||
| if (typeof _efs === "function") { | ||
| return _efs(this.entryName); | ||
| } else { | ||
| return _efs; | ||
| } | ||
| }, | ||
| get extra() { | ||
| return _extra; | ||
| }, | ||
| set extra(val) { | ||
| _extra = val; | ||
| _centralHeader.extraLength = val.length; | ||
| parseExtra(val); | ||
| }, | ||
| get comment() { | ||
| return decoder.decode(_comment); | ||
| }, | ||
| set comment(val) { | ||
| _comment = Utils.toBuffer(val, decoder.encode); | ||
| _centralHeader.commentLength = _comment.length; | ||
| if (_comment.length > 0xffff) throw Utils.Errors.COMMENT_TOO_LONG(); | ||
| }, | ||
| get name() { | ||
| var n = decoder.decode(_entryName); | ||
| return _isDirectory | ||
| ? n | ||
| .substr(n.length - 1) | ||
| .split("/") | ||
| .pop() | ||
| : n.split("/").pop(); | ||
| }, | ||
| get isDirectory() { | ||
| return _isDirectory; | ||
| }, | ||
| getCompressedData: function () { | ||
| return compress(false, null); | ||
| }, | ||
| getCompressedDataAsync: function (/*Function*/ callback) { | ||
| compress(true, callback); | ||
| }, | ||
| setData: function (value) { | ||
| uncompressedData = Utils.toBuffer(value, Utils.decoder.encode); | ||
| if (!_isDirectory && uncompressedData.length) { | ||
| _centralHeader.size = uncompressedData.length; | ||
| _centralHeader.method = Utils.Constants.DEFLATED; | ||
| _centralHeader.crc = Utils.crc32(value); | ||
| _centralHeader.changed = true; | ||
| } else { | ||
| // folders and blank files should be stored | ||
| _centralHeader.method = Utils.Constants.STORED; | ||
| } | ||
| }, | ||
| getData: function (pass) { | ||
| if (_centralHeader.changed) { | ||
| return uncompressedData; | ||
| } else { | ||
| return decompress(false, null, pass); | ||
| } | ||
| }, | ||
| getDataAsync: function (/*Function*/ callback, pass) { | ||
| if (_centralHeader.changed) { | ||
| callback(uncompressedData); | ||
| } else { | ||
| decompress(true, callback, pass); | ||
| } | ||
| }, | ||
| set attr(attr) { | ||
| _centralHeader.attr = attr; | ||
| }, | ||
| get attr() { | ||
| return _centralHeader.attr; | ||
| }, | ||
| set header(/*Buffer*/ data) { | ||
| _centralHeader.loadFromBinary(data); | ||
| }, | ||
| get header() { | ||
| return _centralHeader; | ||
| }, | ||
| packCentralHeader: function () { | ||
| _centralHeader.flags_efs = this.efs; | ||
| _centralHeader.extraLength = _extra.length; | ||
| // 1. create header (buffer) | ||
| var header = _centralHeader.centralHeaderToBinary(); | ||
| var addpos = Utils.Constants.CENHDR; | ||
| // 2. add file name | ||
| _entryName.copy(header, addpos); | ||
| addpos += _entryName.length; | ||
| // 3. add extra data | ||
| _extra.copy(header, addpos); | ||
| addpos += _centralHeader.extraLength; | ||
| // 4. add file comment | ||
| _comment.copy(header, addpos); | ||
| return header; | ||
| }, | ||
| packLocalHeader: function () { | ||
| let addpos = 0; | ||
| _centralHeader.flags_efs = this.efs; | ||
| _centralHeader.extraLocalLength = _extralocal.length; | ||
| // 1. construct local header Buffer | ||
| const localHeaderBuf = _centralHeader.localHeaderToBinary(); | ||
| // 2. localHeader - crate header buffer | ||
| const localHeader = Buffer.alloc(localHeaderBuf.length + _entryName.length + _centralHeader.extraLocalLength); | ||
| // 2.1 add localheader | ||
| localHeaderBuf.copy(localHeader, addpos); | ||
| addpos += localHeaderBuf.length; | ||
| // 2.2 add file name | ||
| _entryName.copy(localHeader, addpos); | ||
| addpos += _entryName.length; | ||
| // 2.3 add extra field | ||
| _extralocal.copy(localHeader, addpos); | ||
| addpos += _extralocal.length; | ||
| return localHeader; | ||
| }, | ||
| toJSON: function () { | ||
| const bytes = function (nr) { | ||
| return "<" + ((nr && nr.length + " bytes buffer") || "null") + ">"; | ||
| }; | ||
| return { | ||
| entryName: this.entryName, | ||
| name: this.name, | ||
| comment: this.comment, | ||
| isDirectory: this.isDirectory, | ||
| header: _centralHeader.toJSON(), | ||
| compressedData: bytes(input), | ||
| data: bytes(uncompressedData) | ||
| }; | ||
| }, | ||
| toString: function () { | ||
| return JSON.stringify(this.toJSON(), null, "\t"); | ||
| } | ||
| }; | ||
| }; |
| const ZipEntry = require("./zipEntry"); | ||
| const Headers = require("./headers"); | ||
| const Utils = require("./util"); | ||
| module.exports = function (/*Buffer|null*/ inBuffer, /** object */ options) { | ||
| var entryList = [], | ||
| entryTable = {}, | ||
| _comment = Buffer.alloc(0), | ||
| mainHeader = new Headers.MainHeader(), | ||
| loadedEntries = false; | ||
| var password = null; | ||
| const temporary = new Set(); | ||
| // assign options | ||
| const opts = options; | ||
| const { noSort, decoder } = opts; | ||
| if (inBuffer) { | ||
| // is a memory buffer | ||
| readMainHeader(opts.readEntries); | ||
| } else { | ||
| // none. is a new file | ||
| loadedEntries = true; | ||
| } | ||
| function makeTemporaryFolders() { | ||
| const foldersList = new Set(); | ||
| // Make list of all folders in file | ||
| for (const elem of Object.keys(entryTable)) { | ||
| const elements = elem.split("/"); | ||
| elements.pop(); // filename | ||
| if (!elements.length) continue; // no folders | ||
| for (let i = 0; i < elements.length; i++) { | ||
| const sub = elements.slice(0, i + 1).join("/") + "/"; | ||
| foldersList.add(sub); | ||
| } | ||
| } | ||
| // create missing folders as temporary | ||
| for (const elem of foldersList) { | ||
| if (!(elem in entryTable)) { | ||
| const tempfolder = new ZipEntry(opts); | ||
| tempfolder.entryName = elem; | ||
| tempfolder.attr = 0x10; | ||
| tempfolder.temporary = true; | ||
| entryList.push(tempfolder); | ||
| entryTable[tempfolder.entryName] = tempfolder; | ||
| temporary.add(tempfolder); | ||
| } | ||
| } | ||
| } | ||
| function readEntries() { | ||
| loadedEntries = true; | ||
| entryTable = {}; | ||
| if (mainHeader.diskEntries > (inBuffer.length - mainHeader.offset) / Utils.Constants.CENHDR) { | ||
| throw Utils.Errors.DISK_ENTRY_TOO_LARGE(); | ||
| } | ||
| entryList = new Array(mainHeader.diskEntries); // total number of entries | ||
| var index = mainHeader.offset; // offset of first CEN header | ||
| for (var i = 0; i < entryList.length; i++) { | ||
| var tmp = index, | ||
| entry = new ZipEntry(opts, inBuffer); | ||
| entry.header = inBuffer.slice(tmp, (tmp += Utils.Constants.CENHDR)); | ||
| entry.entryName = inBuffer.slice(tmp, (tmp += entry.header.fileNameLength)); | ||
| if (entry.header.extraLength) { | ||
| entry.extra = inBuffer.slice(tmp, (tmp += entry.header.extraLength)); | ||
| } | ||
| if (entry.header.commentLength) entry.comment = inBuffer.slice(tmp, tmp + entry.header.commentLength); | ||
| index += entry.header.centralHeaderSize; | ||
| entryList[i] = entry; | ||
| entryTable[entry.entryName] = entry; | ||
| } | ||
| temporary.clear(); | ||
| makeTemporaryFolders(); | ||
| } | ||
| function readMainHeader(/*Boolean*/ readNow) { | ||
| var i = inBuffer.length - Utils.Constants.ENDHDR, // END header size | ||
| max = Math.max(0, i - 0xffff), // 0xFFFF is the max zip file comment length | ||
| n = max, | ||
| endStart = inBuffer.length, | ||
| endOffset = -1, // Start offset of the END header | ||
| commentEnd = 0; | ||
| // option to search header form entire file | ||
| const trailingSpace = typeof opts.trailingSpace === "boolean" ? opts.trailingSpace : false; | ||
| if (trailingSpace) max = 0; | ||
| for (i; i >= n; i--) { | ||
| if (inBuffer[i] !== 0x50) continue; // quick check that the byte is 'P' | ||
| if (inBuffer.readUInt32LE(i) === Utils.Constants.ENDSIG) { | ||
| // "PK\005\006" | ||
| endOffset = i; | ||
| commentEnd = i; | ||
| endStart = i + Utils.Constants.ENDHDR; | ||
| // We already found a regular signature, let's look just a bit further to check if there's any zip64 signature | ||
| n = i - Utils.Constants.END64HDR; | ||
| continue; | ||
| } | ||
| if (inBuffer.readUInt32LE(i) === Utils.Constants.END64SIG) { | ||
| // Found a zip64 signature, let's continue reading the whole zip64 record | ||
| n = max; | ||
| continue; | ||
| } | ||
| if (inBuffer.readUInt32LE(i) === Utils.Constants.ZIP64SIG) { | ||
| // Found the zip64 record, let's determine it's size | ||
| endOffset = i; | ||
| endStart = i + Utils.readBigUInt64LE(inBuffer, i + Utils.Constants.ZIP64SIZE) + Utils.Constants.ZIP64LEAD; | ||
| break; | ||
| } | ||
| } | ||
| if (endOffset == -1) throw Utils.Errors.INVALID_FORMAT(); | ||
| mainHeader.loadFromBinary(inBuffer.slice(endOffset, endStart)); | ||
| if (mainHeader.commentLength) { | ||
| _comment = inBuffer.slice(commentEnd + Utils.Constants.ENDHDR); | ||
| } | ||
| if (readNow) readEntries(); | ||
| } | ||
| function sortEntries() { | ||
| if (entryList.length > 1 && !noSort) { | ||
| entryList.sort((a, b) => a.entryName.toLowerCase().localeCompare(b.entryName.toLowerCase())); | ||
| } | ||
| } | ||
| return { | ||
| /** | ||
| * Returns an array of ZipEntry objects existent in the current opened archive | ||
| * @return Array | ||
| */ | ||
| get entries() { | ||
| if (!loadedEntries) { | ||
| readEntries(); | ||
| } | ||
| return entryList.filter((e) => !temporary.has(e)); | ||
| }, | ||
| /** | ||
| * Archive comment | ||
| * @return {String} | ||
| */ | ||
| get comment() { | ||
| return decoder.decode(_comment); | ||
| }, | ||
| set comment(val) { | ||
| _comment = Utils.toBuffer(val, decoder.encode); | ||
| mainHeader.commentLength = _comment.length; | ||
| }, | ||
| getEntryCount: function () { | ||
| if (!loadedEntries) { | ||
| return mainHeader.diskEntries; | ||
| } | ||
| return entryList.length; | ||
| }, | ||
| forEach: function (callback) { | ||
| this.entries.forEach(callback); | ||
| }, | ||
| /** | ||
| * Returns a reference to the entry with the given name or null if entry is inexistent | ||
| * | ||
| * @param entryName | ||
| * @return ZipEntry | ||
| */ | ||
| getEntry: function (/*String*/ entryName) { | ||
| if (!loadedEntries) { | ||
| readEntries(); | ||
| } | ||
| return entryTable[entryName] || null; | ||
| }, | ||
| /** | ||
| * Adds the given entry to the entry list | ||
| * | ||
| * @param entry | ||
| */ | ||
| setEntry: function (/*ZipEntry*/ entry) { | ||
| if (!loadedEntries) { | ||
| readEntries(); | ||
| } | ||
| entryList.push(entry); | ||
| entryTable[entry.entryName] = entry; | ||
| mainHeader.totalEntries = entryList.length; | ||
| }, | ||
| /** | ||
| * Removes the file with the given name from the entry list. | ||
| * | ||
| * If the entry is a directory, then all nested files and directories will be removed | ||
| * @param entryName | ||
| * @returns {void} | ||
| */ | ||
| deleteFile: function (/*String*/ entryName, withsubfolders = true) { | ||
| if (!loadedEntries) { | ||
| readEntries(); | ||
| } | ||
| const entry = entryTable[entryName]; | ||
| const list = this.getEntryChildren(entry, withsubfolders).map((child) => child.entryName); | ||
| list.forEach(this.deleteEntry); | ||
| }, | ||
| /** | ||
| * Removes the entry with the given name from the entry list. | ||
| * | ||
| * @param {string} entryName | ||
| * @returns {void} | ||
| */ | ||
| deleteEntry: function (/*String*/ entryName) { | ||
| if (!loadedEntries) { | ||
| readEntries(); | ||
| } | ||
| const entry = entryTable[entryName]; | ||
| const index = entryList.indexOf(entry); | ||
| if (index >= 0) { | ||
| entryList.splice(index, 1); | ||
| delete entryTable[entryName]; | ||
| mainHeader.totalEntries = entryList.length; | ||
| } | ||
| }, | ||
| /** | ||
| * Iterates and returns all nested files and directories of the given entry | ||
| * | ||
| * @param entry | ||
| * @return Array | ||
| */ | ||
| getEntryChildren: function (/*ZipEntry*/ entry, subfolders = true) { | ||
| if (!loadedEntries) { | ||
| readEntries(); | ||
| } | ||
| if (typeof entry === "object") { | ||
| if (entry.isDirectory && subfolders) { | ||
| const list = []; | ||
| const name = entry.entryName; | ||
| for (const zipEntry of entryList) { | ||
| if (zipEntry.entryName.startsWith(name)) { | ||
| list.push(zipEntry); | ||
| } | ||
| } | ||
| return list; | ||
| } else { | ||
| return [entry]; | ||
| } | ||
| } | ||
| return []; | ||
| }, | ||
| /** | ||
| * How many child elements entry has | ||
| * | ||
| * @param {ZipEntry} entry | ||
| * @return {integer} | ||
| */ | ||
| getChildCount: function (entry) { | ||
| if (entry && entry.isDirectory) { | ||
| const list = this.getEntryChildren(entry); | ||
| return list.includes(entry) ? list.length - 1 : list.length; | ||
| } | ||
| return 0; | ||
| }, | ||
| /** | ||
| * Returns the zip file | ||
| * | ||
| * @return Buffer | ||
| */ | ||
| compressToBuffer: function () { | ||
| if (!loadedEntries) { | ||
| readEntries(); | ||
| } | ||
| sortEntries(); | ||
| const dataBlock = []; | ||
| const headerBlocks = []; | ||
| let totalSize = 0; | ||
| let dindex = 0; | ||
| mainHeader.size = 0; | ||
| mainHeader.offset = 0; | ||
| let totalEntries = 0; | ||
| for (const entry of this.entries) { | ||
| // compress data and set local and entry header accordingly. Reason why is called first | ||
| const compressedData = entry.getCompressedData(); | ||
| entry.header.offset = dindex; | ||
| // 1. construct local header | ||
| const localHeader = entry.packLocalHeader(); | ||
| // 2. offsets | ||
| const dataLength = localHeader.length + compressedData.length; | ||
| dindex += dataLength; | ||
| // 3. store values in sequence | ||
| dataBlock.push(localHeader); | ||
| dataBlock.push(compressedData); | ||
| // 4. construct central header | ||
| const centralHeader = entry.packCentralHeader(); | ||
| headerBlocks.push(centralHeader); | ||
| // 5. update main header | ||
| mainHeader.size += centralHeader.length; | ||
| totalSize += dataLength + centralHeader.length; | ||
| totalEntries++; | ||
| } | ||
| totalSize += mainHeader.mainHeaderSize; // also includes zip file comment length | ||
| // point to end of data and beginning of central directory first record | ||
| mainHeader.offset = dindex; | ||
| mainHeader.totalEntries = totalEntries; | ||
| dindex = 0; | ||
| const outBuffer = Buffer.alloc(totalSize); | ||
| // write data blocks | ||
| for (const content of dataBlock) { | ||
| content.copy(outBuffer, dindex); | ||
| dindex += content.length; | ||
| } | ||
| // write central directory entries | ||
| for (const content of headerBlocks) { | ||
| content.copy(outBuffer, dindex); | ||
| dindex += content.length; | ||
| } | ||
| // write main header | ||
| const mh = mainHeader.toBinary(); | ||
| if (_comment) { | ||
| _comment.copy(mh, Utils.Constants.ENDHDR); // add zip file comment | ||
| } | ||
| mh.copy(outBuffer, dindex); | ||
| // Since we update entry and main header offsets, | ||
| // they are no longer valid and we have to reset content | ||
| // (Issue 64) | ||
| inBuffer = outBuffer; | ||
| loadedEntries = false; | ||
| return outBuffer; | ||
| }, | ||
| toAsyncBuffer: function (/*Function*/ onSuccess, /*Function*/ onFail, /*Function*/ onItemStart, /*Function*/ onItemEnd) { | ||
| try { | ||
| if (!loadedEntries) { | ||
| readEntries(); | ||
| } | ||
| sortEntries(); | ||
| const dataBlock = []; | ||
| const centralHeaders = []; | ||
| let totalSize = 0; | ||
| let dindex = 0; | ||
| let totalEntries = 0; | ||
| mainHeader.size = 0; | ||
| mainHeader.offset = 0; | ||
| const compress2Buffer = function (entryLists) { | ||
| if (entryLists.length > 0) { | ||
| const entry = entryLists.shift(); | ||
| const name = entry.entryName + entry.extra.toString(); | ||
| if (onItemStart) onItemStart(name); | ||
| entry.getCompressedDataAsync(function (compressedData) { | ||
| if (onItemEnd) onItemEnd(name); | ||
| entry.header.offset = dindex; | ||
| // 1. construct local header | ||
| const localHeader = entry.packLocalHeader(); | ||
| // 2. offsets | ||
| const dataLength = localHeader.length + compressedData.length; | ||
| dindex += dataLength; | ||
| // 3. store values in sequence | ||
| dataBlock.push(localHeader); | ||
| dataBlock.push(compressedData); | ||
| // central header | ||
| const centalHeader = entry.packCentralHeader(); | ||
| centralHeaders.push(centalHeader); | ||
| mainHeader.size += centalHeader.length; | ||
| totalSize += dataLength + centalHeader.length; | ||
| totalEntries++; | ||
| compress2Buffer(entryLists); | ||
| }); | ||
| } else { | ||
| totalSize += mainHeader.mainHeaderSize; // also includes zip file comment length | ||
| // point to end of data and beginning of central directory first record | ||
| mainHeader.offset = dindex; | ||
| mainHeader.totalEntries = totalEntries; | ||
| dindex = 0; | ||
| const outBuffer = Buffer.alloc(totalSize); | ||
| dataBlock.forEach(function (content) { | ||
| content.copy(outBuffer, dindex); // write data blocks | ||
| dindex += content.length; | ||
| }); | ||
| centralHeaders.forEach(function (content) { | ||
| content.copy(outBuffer, dindex); // write central directory entries | ||
| dindex += content.length; | ||
| }); | ||
| const mh = mainHeader.toBinary(); | ||
| if (_comment) { | ||
| _comment.copy(mh, Utils.Constants.ENDHDR); // add zip file comment | ||
| } | ||
| mh.copy(outBuffer, dindex); // write main header | ||
| // Since we update entry and main header offsets, they are no | ||
| // longer valid and we have to reset content using our new buffer | ||
| // (Issue 64) | ||
| inBuffer = outBuffer; | ||
| loadedEntries = false; | ||
| onSuccess(outBuffer); | ||
| } | ||
| }; | ||
| compress2Buffer(Array.from(this.entries)); | ||
| } catch (e) { | ||
| onFail(e); | ||
| } | ||
| } | ||
| }; | ||
| }; |
| { | ||
| "name": "foundry-local-sdk", | ||
| "version": "1.2.3", | ||
| "description": "Foundry Local JavaScript SDK", | ||
| "main": "dist/index.js", | ||
| "types": "dist/index.d.ts", | ||
| "type": "module", | ||
| "files": [ | ||
| "dist", | ||
| "prebuilds", | ||
| "script/install-standard.cjs", | ||
| "script/install-utils.cjs", | ||
| "script/preinstall.cjs", | ||
| "deps_versions.json" | ||
| ], | ||
| "scripts": { | ||
| "build": "tsc -p tsconfig.build.json", | ||
| "build:native": "cd native && node-gyp rebuild && node ../script/copy-addon.cjs", | ||
| "docs": "typedoc", | ||
| "example": "tsx examples/chat-completion.ts", | ||
| "install": "node script/install-standard.cjs", | ||
| "pack": "node script/pack.cjs", | ||
| "pack:winml": "node script/pack.cjs winml", | ||
| "preinstall": "node script/preinstall.cjs", | ||
| "test": "mocha --import=tsx test/**/*.test.ts" | ||
| }, | ||
| "dependencies": { | ||
| "adm-zip": "^0.5.16" | ||
| }, | ||
| "devDependencies": { | ||
| "@types/chai": "^5.2.3", | ||
| "@types/mocha": "^10.0.10", | ||
| "@types/node": "^24.10.1", | ||
| "chai": "^6.2.1", | ||
| "mocha": "^11.7.5", | ||
| "node-api-headers": "^1.8.0", | ||
| "tsx": "^4.7.0", | ||
| "typedoc": "^0.28.15", | ||
| "typedoc-plugin-markdown": "^4.2.0", | ||
| "typescript": "^5.9.3" | ||
| }, | ||
| "directories": { | ||
| "doc": "docs", | ||
| "example": "examples", | ||
| "test": "test" | ||
| }, | ||
| "author": "", | ||
| "license": "MIT" | ||
| } |
| # Foundry Local JS SDK | ||
| The Foundry Local JS SDK provides a JavaScript/TypeScript interface for running AI models locally on your machine. Discover, download, load, and run inference — all without cloud dependencies. | ||
| ## Features | ||
| - **Local-first AI** — Run models entirely on your machine with no cloud calls | ||
| - **Model catalog** — Browse and discover available models, check what's cached or loaded | ||
| - **Automatic model management** — Download, load, unload, and remove models from cache | ||
| - **Chat completions** — OpenAI-compatible chat API with both synchronous and streaming responses | ||
| - **Embeddings** — Generate text embeddings via OpenAI-compatible API | ||
| - **Audio transcription** — Transcribe audio files locally with streaming support | ||
| - **Multi-variant models** — Models can have multiple variants (e.g., different quantizations) with automatic selection of the best cached variant | ||
| - **Embedded web service** — Start a local HTTP service for OpenAI-compatible API access | ||
| - **WinML support** — Automatic execution provider download on Windows for NPU/GPU acceleration | ||
| - **Configurable inference** — Control temperature, max tokens, top-k, top-p, frequency penalty, and more | ||
| ## Installation | ||
| ```bash | ||
| npm install foundry-local-sdk | ||
| ``` | ||
| ## TypeScript support | ||
| The package is authored in TypeScript and ships with bundled type declarations (`.d.ts` files) alongside the compiled JavaScript. No `@types/foundry-local-sdk` package or manual ambient declarations are needed. | ||
| Importing from `foundry-local-sdk` in a TypeScript project gives you full type information and IntelliSense for every public API, including `FoundryLocalManager`, `Catalog`, `ChatClient`, `AudioClient`, `EmbeddingClient`, `ResponsesClient`, `LiveAudioTranscriptionSession`, and all of their associated option and response types. | ||
| ## WinML: Automatic Hardware Acceleration (Windows) | ||
| On Windows, install the WinML package to enable automatic execution provider management. The SDK can discover, download, and register hardware-specific execution providers (e.g., Qualcomm QNN for NPU acceleration) without manual driver or EP setup. | ||
| > **Note:** `foundry-local-sdk-winml` is a Windows-only package. Its install script downloads WinML artifacts during installation and may fail on macOS or Linux. | ||
| ```bash | ||
| npm install foundry-local-sdk-winml | ||
| ``` | ||
| To use a newer Windows ML runtime DLL, set `FOUNDRY_LOCAL_WINDOWS_AI_MACHINELEARNING_VERSION` before installing or rebuilding `foundry-local-sdk-winml`; the install script downloads `Microsoft.Windows.AI.MachineLearning.dll` from that NuGet version. | ||
| When WinML is enabled: | ||
| - Execution providers like `QNNExecutionProvider`, `OpenVINOExecutionProvider`, etc. are downloaded and registered on the fly, enabling NPU/GPU acceleration without manual configuration | ||
| - **No code changes needed** — your application code stays the same whether WinML is enabled or not | ||
| ### Explicit EP Management | ||
| You can explicitly discover and download execution providers using the `discoverEps()` and `downloadAndRegisterEps()` methods: | ||
| ```typescript | ||
| // Discover available EPs and their status | ||
| const eps = manager.discoverEps(); | ||
| for (const ep of eps) { | ||
| console.log(`${ep.name} — registered: ${ep.isRegistered}`); | ||
| } | ||
| // Download and register all available EPs | ||
| const result = await manager.downloadAndRegisterEps(); | ||
| console.log(`Success: ${result.success}, Status: ${result.status}`); | ||
| // Download only specific EPs | ||
| const result2 = await manager.downloadAndRegisterEps([eps[0].name]); | ||
| ``` | ||
| #### Per-EP download progress | ||
| Pass an optional `progressCallback` to receive `(epName, percent)` updates as each EP downloads (`percent` is 0–100): | ||
| ```typescript | ||
| let currentEp = ''; | ||
| await manager.downloadAndRegisterEps((epName, percent) => { | ||
| if (epName !== currentEp) { | ||
| if (currentEp !== '') { | ||
| process.stdout.write('\n'); | ||
| } | ||
| currentEp = epName; | ||
| } | ||
| process.stdout.write(`\r ${epName} ${percent.toFixed(1)}%`); | ||
| }); | ||
| process.stdout.write('\n'); | ||
| ``` | ||
| #### Cancelling model and EP downloads | ||
| Use an `AbortController` with either `downloadAndRegisterEps()` or `model.download()`. Aborting the signal rejects the in-progress download promise. | ||
| ```typescript | ||
| // manager and model already initialized | ||
| const controller = new AbortController(); | ||
| setTimeout(() => controller.abort(), 5000); | ||
| await manager.downloadAndRegisterEps(controller.signal); | ||
| await model.download(controller.signal); | ||
| ``` | ||
| Catalog access does not block on EP downloads. Call `downloadAndRegisterEps()` when you need hardware-accelerated execution providers. | ||
| ## Quick Start | ||
| ```typescript | ||
| import { FoundryLocalManager } from 'foundry-local-sdk'; | ||
| const manager = FoundryLocalManager.create({ | ||
| appName: 'foundry_local_samples', | ||
| logLevel: 'info' | ||
| }); | ||
| // Get the model object | ||
| const modelAlias = 'qwen2.5-0.5b'; | ||
| const model = await manager.catalog.getModel(modelAlias); | ||
| // Download the model | ||
| console.log(`\nDownloading model ${modelAlias}...`); | ||
| await model.download((progress) => { | ||
| process.stdout.write(`\rDownloading... ${progress.toFixed(2)}%`); | ||
| }); | ||
| // Load the model | ||
| await model.load(); | ||
| // Create chat client | ||
| const chatClient = model.createChatClient(); | ||
| // Example chat completion | ||
| console.log('\nTesting chat completion...'); | ||
| const completion = await chatClient.completeChat([ | ||
| { role: 'user', content: 'Why is the sky blue?' } | ||
| ]); | ||
| console.log(completion.choices[0]?.message?.content); | ||
| // Example streaming completion | ||
| console.log('\nTesting streaming completion...'); | ||
| for await (const chunk of chatClient.completeStreamingChat( | ||
| [{ role: 'user', content: 'Write a short poem about programming.' }] | ||
| )) { | ||
| const content = chunk.choices?.[0]?.delta?.content; | ||
| if (content) { | ||
| process.stdout.write(content); | ||
| } | ||
| } | ||
| console.log('\n'); | ||
| // Unload the model | ||
| await model.unload(); | ||
| ``` | ||
| ## Usage | ||
| ### Browsing the Model Catalog | ||
| The `Catalog` lets you discover what models are available, which are already cached locally, and which are currently loaded in memory. | ||
| ```typescript | ||
| const catalog = manager.catalog; | ||
| // List all available models | ||
| const models = await catalog.getModels(); | ||
| models.forEach(model => { | ||
| console.log(`${model.alias} — cached: ${model.isCached}`); | ||
| }); | ||
| // See what's already downloaded | ||
| const cached = await catalog.getCachedModels(); | ||
| // See what's currently loaded in memory | ||
| const loaded = await catalog.getLoadedModels(); | ||
| ``` | ||
| ### Loading and Running Models | ||
| Each model can have multiple variants (different quantizations or formats). The SDK automatically selects the best available variant, preferring cached versions. All models implement the `IModel` interface. | ||
| ```typescript | ||
| const model = await catalog.getModel('qwen2.5-0.5b'); | ||
| // Download if not cached (with optional progress tracking) | ||
| if (!model.isCached) { | ||
| await model.download((progress) => { | ||
| console.log(`Download: ${progress}%`); | ||
| }); | ||
| } | ||
| // Load into memory and run inference | ||
| await model.load(); | ||
| const chatClient = model.createChatClient(); | ||
| ``` | ||
| You can also select a specific variant manually: | ||
| ```typescript | ||
| const variants = model.variants; | ||
| model.selectVariant(variants[0]); | ||
| ``` | ||
| ### Chat Completions | ||
| The `ChatClient` follows the OpenAI Chat Completion API structure. | ||
| ```typescript | ||
| const chatClient = model.createChatClient(); | ||
| // Configure settings | ||
| chatClient.settings.temperature = 0.7; | ||
| chatClient.settings.maxTokens = 800; | ||
| chatClient.settings.topP = 0.9; | ||
| // Synchronous completion | ||
| const response = await chatClient.completeChat([ | ||
| { role: 'system', content: 'You are a helpful assistant.' }, | ||
| { role: 'user', content: 'Explain quantum computing in simple terms.' } | ||
| ]); | ||
| console.log(response.choices[0].message.content); | ||
| ``` | ||
| ### Streaming Responses | ||
| For real-time output, use streaming: | ||
| ```typescript | ||
| for await (const chunk of chatClient.completeStreamingChat( | ||
| [{ role: 'user', content: 'Write a short poem about programming.' }] | ||
| )) { | ||
| const content = chunk.choices?.[0]?.delta?.content; | ||
| if (content) { | ||
| process.stdout.write(content); | ||
| } | ||
| } | ||
| ``` | ||
| ### Embeddings | ||
| Generate text embeddings using the `EmbeddingClient`: | ||
| ```typescript | ||
| const embeddingClient = model.createEmbeddingClient(); | ||
| // Single input | ||
| const response = await embeddingClient.generateEmbedding( | ||
| 'The quick brown fox jumps over the lazy dog' | ||
| ); | ||
| const embedding = response.data[0].embedding; // number[] | ||
| console.log(`Dimensions: ${embedding.length}`); | ||
| // Batch input | ||
| const batchResponse = await embeddingClient.generateEmbeddings([ | ||
| 'The quick brown fox', | ||
| 'The capital of France is Paris' | ||
| ]); | ||
| // batchResponse.data[0].embedding, batchResponse.data[1].embedding | ||
| ``` | ||
| ### Audio Transcription | ||
| Transcribe audio files locally using the `AudioClient`: | ||
| ```typescript | ||
| const audioClient = model.createAudioClient(); | ||
| audioClient.settings.language = 'en'; | ||
| // Synchronous transcription | ||
| const result = await audioClient.transcribe('/path/to/audio.wav'); | ||
| // Streaming transcription | ||
| for await (const chunk of audioClient.transcribeStreaming('/path/to/audio.wav')) { | ||
| console.log(chunk); | ||
| } | ||
| ``` | ||
| ### Embedded Web Service | ||
| Start a local HTTP server that exposes an OpenAI-compatible API: | ||
| ```typescript | ||
| manager.startWebService(); | ||
| console.log('Service running at:', manager.urls); | ||
| // Use with any OpenAI-compatible client library | ||
| // ... | ||
| manager.stopWebService(); | ||
| ``` | ||
| ### Configuration | ||
| The SDK is configured via `FoundryLocalConfig` when creating the manager: | ||
| | Option | Description | Default | | ||
| |--------|-------------|---------| | ||
| | `appName` | **Required.** Application name for logs and telemetry. | — | | ||
| | `appDataDir` | Directory where application data should be stored | `~/.{appName}` | | ||
| | `logLevel` | Logging level: `trace`, `debug`, `info`, `warn`, `error`, `fatal` | `warn` | | ||
| | `modelCacheDir` | Directory for downloaded models | `~/.{appName}/cache/models` | | ||
| | `logsDir` | Directory for log files | `~/.{appName}/logs` | | ||
| | `libraryPath` | Path to native Foundry Local Core libraries | Auto-discovered | | ||
| | `serviceEndpoint` | URL of an existing external service to connect to | — | | ||
| | `webServiceUrls` | URL(s) for the embedded web service to bind to | — | | ||
| ## API Reference | ||
| Auto-generated class documentation lives in [`docs/classes/`](docs/classes/): | ||
| - [FoundryLocalManager](docs/classes/FoundryLocalManager.md) — SDK entry point, web service management | ||
| - [Catalog](docs/classes/Catalog.md) — Model discovery and browsing | ||
| - [IModel](docs/README.md#imodel) — Model interface: variant selection, download, load, inference | ||
| - [ChatClient](docs/classes/ChatClient.md) — Chat completions (sync and streaming) | ||
| - [AudioClient](docs/classes/AudioClient.md) — Audio transcription (sync and streaming) | ||
| - [ModelLoadManager](docs/classes/ModelLoadManager.md) — Low-level model loading management | ||
| ## Contributing: Building from Source | ||
| ### Prerequisites | ||
| - **Node.js 20+** | ||
| - **Python 3.x** — required by `node-gyp` for compiling the native addon | ||
| - **C/C++ toolchain**: | ||
| - **Windows**: Visual Studio Build Tools (the "Desktop development with C++" workload) | ||
| - **Linux**: `build-essential` (`apt install build-essential`) | ||
| - **macOS**: Xcode Command Line Tools (`xcode-select --install`) | ||
| ### Build Steps | ||
| ```bash | ||
| # 1. Install JS dependencies (also downloads native core binaries) | ||
| npm install | ||
| # 2. Build the Node-API native addon (compiles C code and copies to prebuilds/) | ||
| npm run build:native | ||
| # 3. Build the TypeScript source | ||
| npm run build | ||
| # 4. Run tests | ||
| npm test | ||
| # 5. Pack the SDK into a .tgz (includes prebuilt addon for your platform) | ||
| npm run pack | ||
| ``` | ||
| > **Note:** `npm run build:native` compiles the addon only for your current platform. The published npm package includes prebuilt addons for all supported platforms (win32-x64, win32-arm64, linux-x64, linux-arm64, darwin-arm64), which are compiled in CI. | ||
| ## Running Tests | ||
| ```bash | ||
| npm test | ||
| ``` | ||
| See `test/README.md` for details on prerequisites and setup. | ||
| ## Running Examples | ||
| ```bash | ||
| npm run example | ||
| ``` | ||
| This runs the chat completion example in `examples/chat-completion.ts`. |
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
| // Shared NuGet download and extraction utilities for install scripts. | ||
| 'use strict'; | ||
| const fs = require('fs'); | ||
| const path = require('path'); | ||
| const os = require('os'); | ||
| const https = require('https'); | ||
| const AdmZip = require('adm-zip'); | ||
| const PLATFORM_MAP = { | ||
| 'win32-x64': 'win-x64', | ||
| 'win32-arm64': 'win-arm64', | ||
| 'linux-x64': 'linux-x64', | ||
| 'linux-arm64': 'linux-arm64', | ||
| 'darwin-arm64': 'osx-arm64', | ||
| }; | ||
| const platformKey = `${os.platform()}-${os.arch()}`; | ||
| const RID = PLATFORM_MAP[platformKey]; | ||
| // Install binaries into foundry-local-core/<platform> inside the package root. | ||
| const BIN_DIR = path.join(__dirname, '..', 'foundry-local-core', platformKey); | ||
| const EXT = os.platform() === 'win32' ? '.dll' : os.platform() === 'darwin' ? '.dylib' : '.so'; | ||
| const REQUIRED_FILES = [ | ||
| `Microsoft.AI.Foundry.Local.Core${EXT}`, | ||
| `${os.platform() === 'win32' ? '' : 'lib'}onnxruntime${EXT}`, | ||
| `${os.platform() === 'win32' ? '' : 'lib'}onnxruntime-genai${EXT}`, | ||
| ]; | ||
| // Feeds tried in order. Primary: nuget.org (stable releases). Fallback: | ||
| // the public ORT-Nightly Azure DevOps NuGet feed (where dev / pre-release | ||
| // builds of Foundry Local Core, ONNX Runtime and ONNX Runtime GenAI live | ||
| // before they reach nuget.org). If a download from a feed fails for any | ||
| // reason, the next feed is tried. | ||
| const FEEDS = [ | ||
| 'https://api.nuget.org/v3/index.json', | ||
| 'https://pkgs.dev.azure.com/aiinfra/PublicPackages/_packaging/ORT-Nightly/nuget/v3/index.json', | ||
| ]; | ||
| // --- Download helpers --- | ||
| async function downloadWithRetryAndRedirects(url, destStream = null) { | ||
| const maxRedirects = 5; | ||
| let currentUrl = url; | ||
| let redirects = 0; | ||
| while (redirects < maxRedirects) { | ||
| const response = await new Promise((resolve, reject) => { | ||
| https.get(currentUrl, (res) => resolve(res)) | ||
| .on('error', reject); | ||
| }); | ||
| if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) { | ||
| currentUrl = response.headers.location; | ||
| response.resume(); | ||
| redirects++; | ||
| console.log(` Following redirect to ${new URL(currentUrl).host}...`); | ||
| continue; | ||
| } | ||
| if (response.statusCode !== 200) { | ||
| throw new Error(`Download failed with status ${response.statusCode}: ${currentUrl}`); | ||
| } | ||
| if (destStream) { | ||
| response.pipe(destStream); | ||
| return new Promise((resolve, reject) => { | ||
| destStream.on('finish', resolve); | ||
| destStream.on('error', reject); | ||
| response.on('error', reject); | ||
| }); | ||
| } else { | ||
| let data = ''; | ||
| response.on('data', chunk => data += chunk); | ||
| return new Promise((resolve, reject) => { | ||
| response.on('end', () => resolve(data)); | ||
| response.on('error', reject); | ||
| }); | ||
| } | ||
| } | ||
| throw new Error('Too many redirects'); | ||
| } | ||
| async function downloadJson(url) { | ||
| return JSON.parse(await downloadWithRetryAndRedirects(url)); | ||
| } | ||
| async function downloadFile(url, dest) { | ||
| const file = fs.createWriteStream(dest); | ||
| try { | ||
| await downloadWithRetryAndRedirects(url, file); | ||
| file.close(); | ||
| } catch (e) { | ||
| file.close(); | ||
| if (fs.existsSync(dest)) fs.unlinkSync(dest); | ||
| throw e; | ||
| } | ||
| } | ||
| const serviceIndexCache = new Map(); | ||
| function expectedFileForPackage(pkgName) { | ||
| const prefix = os.platform() === 'win32' ? '' : 'lib'; | ||
| if (pkgName.includes('Foundry.Local.Core')) { | ||
| return `Microsoft.AI.Foundry.Local.Core${EXT}`; | ||
| } | ||
| if (pkgName.includes('Windows.AI.MachineLearning')) { | ||
| return `Microsoft.Windows.AI.MachineLearning${EXT}`; | ||
| } | ||
| if (pkgName.includes('OnnxRuntimeGenAI')) { | ||
| return `${prefix}onnxruntime-genai${EXT}`; | ||
| } | ||
| if (pkgName.includes('OnnxRuntime')) { | ||
| return `${prefix}onnxruntime${EXT}`; | ||
| } | ||
| return undefined; | ||
| } | ||
| function entryFileName(entry) { | ||
| const normalized = entry.entryName.replace(/\\/g, '/'); | ||
| return normalized.slice(normalized.lastIndexOf('/') + 1); | ||
| } | ||
| function nativeEntriesForRid(zip, includeFiles) { | ||
| const includedNames = includeFiles | ||
| ? new Set(includeFiles.map(name => name.toLowerCase())) | ||
| : null; | ||
| const nativePrefix = `runtimes/${RID}/native/`.toLowerCase(); | ||
| const runtimePrefix = `runtimes/${RID}/`.toLowerCase(); | ||
| return zip.getEntries().filter(e => { | ||
| const p = e.entryName.toLowerCase(); | ||
| if (!p.endsWith(EXT)) { | ||
| return false; | ||
| } | ||
| const inNativePath = p.startsWith(nativePrefix); | ||
| let inRuntimePath = false; | ||
| if (p.startsWith(runtimePrefix)) { | ||
| const relativePath = p.slice(runtimePrefix.length); | ||
| inRuntimePath = relativePath.length > 0 && !relativePath.includes('/'); | ||
| } | ||
| if (!inNativePath && !inRuntimePath) { | ||
| return false; | ||
| } | ||
| if (includedNames && !includedNames.has(entryFileName(e).toLowerCase())) { | ||
| return false; | ||
| } | ||
| return true; | ||
| }); | ||
| } | ||
| function removeFiles(binDir, files) { | ||
| for (const file of files || []) { | ||
| const filePath = path.join(binDir, file); | ||
| if (fs.existsSync(filePath)) { | ||
| fs.rmSync(filePath, { force: true }); | ||
| console.log(` Removed ${file}`); | ||
| } | ||
| } | ||
| } | ||
| async function getBaseAddress(feedUrl) { | ||
| if (!serviceIndexCache.has(feedUrl)) { | ||
| serviceIndexCache.set(feedUrl, await downloadJson(feedUrl)); | ||
| } | ||
| const resources = serviceIndexCache.get(feedUrl).resources || []; | ||
| const res = resources.find(r => r['@type'] && r['@type'].startsWith('PackageBaseAddress/3.0.0')); | ||
| if (!res) throw new Error('Could not find PackageBaseAddress/3.0.0 in NuGet feed.'); | ||
| const baseAddress = res['@id']; | ||
| return baseAddress.endsWith('/') ? baseAddress : baseAddress + '/'; | ||
| } | ||
| async function installPackage(artifact, tempDir, binDir, skipIfPresent) { | ||
| const pkgName = artifact.name; | ||
| const pkgVer = artifact.version; | ||
| // Skip download if this package's main native binary is already present | ||
| // (e.g. pre-populated by CI from a locally-built artifact). | ||
| // Callers pass skipIfPresent=false when overriding (e.g. WinML over standard). | ||
| if (skipIfPresent) { | ||
| const expectedFile = expectedFileForPackage(pkgName); | ||
| if (expectedFile && fs.existsSync(path.join(binDir, expectedFile))) { | ||
| console.log(` ${pkgName}: already present, skipping download.`); | ||
| return; | ||
| } | ||
| } | ||
| // Try each configured feed in order; on failure fall back to the next. | ||
| let lastError; | ||
| for (let i = 0; i < FEEDS.length; i++) { | ||
| const feedUrl = FEEDS[i]; | ||
| const feedHost = new URL(feedUrl).host; | ||
| try { | ||
| const baseAddress = await getBaseAddress(feedUrl); | ||
| const nameLower = pkgName.toLowerCase(); | ||
| const verLower = pkgVer.toLowerCase(); | ||
| const downloadUrl = `${baseAddress}${nameLower}/${verLower}/${nameLower}.${verLower}.nupkg`; | ||
| const nupkgPath = path.join(tempDir, `${pkgName}.${pkgVer}.nupkg`); | ||
| console.log(` Downloading ${pkgName} ${pkgVer} from ${feedHost}...`); | ||
| await downloadFile(downloadUrl, nupkgPath); | ||
| console.log(` Extracting...`); | ||
| const zip = new AdmZip(nupkgPath); | ||
| const entries = nativeEntriesForRid(zip, artifact.includeFiles); | ||
| if (entries.length > 0) { | ||
| entries.forEach(entry => { | ||
| zip.extractEntryTo(entry, binDir, false, true); | ||
| console.log(` Extracted ${entry.name}`); | ||
| }); | ||
| } else { | ||
| console.warn(` No files found for RID ${RID} in ${pkgName}.`); | ||
| } | ||
| removeFiles(binDir, artifact.removeFiles); | ||
| // Write a metadata package.json with version info for diagnostics | ||
| if (pkgName.startsWith('Microsoft.AI.Foundry.Local.Core')) { | ||
| const pkgJsonPath = path.join(binDir, 'package.json'); | ||
| const pkgContent = { | ||
| name: `@foundry-local-core/${platformKey}`, | ||
| version: pkgVer, | ||
| description: `Native binaries for Foundry Local SDK (${platformKey})`, | ||
| private: true | ||
| }; | ||
| fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgContent, null, 2)); | ||
| } | ||
| return; | ||
| } catch (err) { | ||
| lastError = err; | ||
| const isLast = i === FEEDS.length - 1; | ||
| const reason = err instanceof Error ? err.message : String(err); | ||
| if (!isLast) { | ||
| console.warn(` ${pkgName} ${pkgVer}: download from ${feedHost} failed (${reason}); trying next feed...`); | ||
| } | ||
| } | ||
| } | ||
| throw new Error(`Failed to download ${pkgName} ${pkgVer} from any configured feed (${FEEDS.map(f => new URL(f).host).join(', ')}): ${lastError instanceof Error ? lastError.message : lastError}`); | ||
| } | ||
| async function runInstall(artifacts, options) { | ||
| if (!RID) { | ||
| console.warn(`[foundry-local] Unsupported platform: ${platformKey}. Skipping.`); | ||
| return; | ||
| } | ||
| const binDir = (options && options.binDir) || BIN_DIR; | ||
| // When a custom binDir is provided (e.g. WinML overriding standard), | ||
| // don't skip packages whose output files already exist — we need to | ||
| // overwrite them with the variant's binaries. | ||
| const skipIfPresent = !(options && options.binDir); | ||
| console.log(`[foundry-local] Installing native libraries for ${RID}...`); | ||
| fs.mkdirSync(binDir, { recursive: true }); | ||
| const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'foundry-install-')); | ||
| try { | ||
| for (const artifact of artifacts) { | ||
| await installPackage(artifact, tempDir, binDir, skipIfPresent); | ||
| } | ||
| console.log('[foundry-local] Installation complete.'); | ||
| } finally { | ||
| try { fs.rmSync(tempDir, { recursive: true, force: true }); } catch {} | ||
| } | ||
| } | ||
| module.exports = { runInstall }; |
+34
| #!/usr/bin/env node | ||
| /*--------------------------------------------------------------------------------------------- | ||
| * Copyright (c) Microsoft Corporation. All rights reserved. | ||
| *--------------------------------------------------------------------------------------------*/ | ||
| import __module from "module"; | ||
| const require = __module.createRequire(import.meta.url); | ||
| var tt=Object.create;var K=Object.defineProperty;var rt=Object.getOwnPropertyDescriptor;var nt=Object.getOwnPropertyNames;var ot=Object.getPrototypeOf,st=Object.prototype.hasOwnProperty;var X=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(t,r)=>(typeof require<"u"?require:t)[r]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var k=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var it=(e,t,r,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of nt(t))!st.call(e,o)&&o!==r&&K(e,o,{get:()=>t[o],enumerable:!(s=rt(t,o))||s.enumerable});return e};var ct=(e,t,r)=>(r=e!=null?tt(ot(e)):{},it(t||!e||!e.__esModule?K(r,"default",{value:e,enumerable:!0}):r,e));var de=k((wr,le)=>{"use strict";var ue=()=>process.platform==="linux",N=null,Pt=()=>{if(!N)if(ue()&&process.report){let e=process.report.excludeNetwork;process.report.excludeNetwork=!0,N=process.report.getReport(),process.report.excludeNetwork=e}else N={};return N};le.exports={isLinux:ue,getReport:Pt}});var pe=k((xr,fe)=>{"use strict";var _=X("fs"),Ot="/usr/bin/ldd",wt="/proc/self/exe",A=2048,xt=e=>{let t=_.openSync(e,"r"),r=Buffer.alloc(A),s=_.readSync(t,r,0,A,0);return _.close(t,()=>{}),r.subarray(0,s)},Tt=e=>new Promise((t,r)=>{_.open(e,"r",(s,o)=>{if(s)r(s);else{let n=Buffer.alloc(A);_.read(o,n,0,A,0,(c,d)=>{t(n.subarray(0,d)),_.close(o,()=>{})})}})});fe.exports={LDD_PATH:Ot,SELF_PATH:wt,readFileSync:xt,readFile:Tt}});var ge=k((Tr,me)=>{"use strict";var It=e=>{if(e.length<64||e.readUInt32BE(0)!==2135247942||e.readUInt8(4)!==2||e.readUInt8(5)!==1)return null;let t=e.readUInt32LE(32),r=e.readUInt16LE(54),s=e.readUInt16LE(56);for(let o=0;o<s;o++){let n=t+o*r;if(e.readUInt32LE(n)===3){let d=e.readUInt32LE(n+8),O=e.readUInt32LE(n+32);return e.subarray(d,d+O).toString().replace(/\0.*$/g,"")}}return null};me.exports={interpreterPath:It}});var Ne=k((Ir,Re)=>{"use strict";var ye=X("child_process"),{isLinux:L,getReport:be}=de(),{LDD_PATH:$,SELF_PATH:ve,readFile:V,readFileSync:W}=pe(),{interpreterPath:Se}=ge(),y,b,v,Ee="getconf GNU_LIBC_VERSION 2>&1 || true; ldd --version 2>&1 || true",T="",Pe=()=>T||new Promise(e=>{ye.exec(Ee,(t,r)=>{T=t?" ":r,e(T)})}),Oe=()=>{if(!T)try{T=ye.execSync(Ee,{encoding:"utf8"})}catch{T=" "}return T},P="glibc",we=/LIBC[a-z0-9 \-).]*?(\d+\.\d+)/i,I="musl",_t=e=>e.includes("libc.musl-")||e.includes("ld-musl-"),xe=()=>{let e=be();return e.header&&e.header.glibcVersionRuntime?P:Array.isArray(e.sharedObjects)&&e.sharedObjects.some(_t)?I:null},Te=e=>{let[t,r]=e.split(/[\r\n]+/);return t&&t.includes(P)?P:r&&r.includes(I)?I:null},Ie=e=>{if(e){if(e.includes("/ld-musl-"))return I;if(e.includes("/ld-linux-"))return P}return null},_e=e=>(e=e.toString(),e.includes("musl")?I:e.includes("GNU C Library")?P:null),Lt=async()=>{if(b!==void 0)return b;b=null;try{let e=await V($);b=_e(e)}catch{}return b},Ft=()=>{if(b!==void 0)return b;b=null;try{let e=W($);b=_e(e)}catch{}return b},Ct=async()=>{if(y!==void 0)return y;y=null;try{let e=await V(ve),t=Se(e);y=Ie(t)}catch{}return y},kt=()=>{if(y!==void 0)return y;y=null;try{let e=W(ve),t=Se(e);y=Ie(t)}catch{}return y},Le=async()=>{let e=null;if(L()&&(e=await Ct(),!e&&(e=await Lt(),e||(e=xe()),!e))){let t=await Pe();e=Te(t)}return e},Fe=()=>{let e=null;if(L()&&(e=kt(),!e&&(e=Ft(),e||(e=xe()),!e))){let t=Oe();e=Te(t)}return e},Rt=async()=>L()&&await Le()!==P,Nt=()=>L()&&Fe()!==P,At=async()=>{if(v!==void 0)return v;v=null;try{let t=(await V($)).match(we);t&&(v=t[1])}catch{}return v},$t=()=>{if(v!==void 0)return v;v=null;try{let t=W($).match(we);t&&(v=t[1])}catch{}return v},Ce=()=>{let e=be();return e.header&&e.header.glibcVersionRuntime?e.header.glibcVersionRuntime:null},he=e=>e.trim().split(/\s+/)[1],ke=e=>{let[t,r,s]=e.split(/[\r\n]+/);return t&&t.includes(P)?he(t):r&&s&&r.includes(I)?he(s):null},Ht=async()=>{let e=null;if(L()&&(e=await At(),e||(e=Ce()),!e)){let t=await Pe();e=ke(t)}return e},Ut=()=>{let e=null;if(L()&&(e=$t(),e||(e=Ce()),!e)){let t=Oe();e=ke(t)}return e};Re.exports={GLIBC:P,MUSL:I,family:Le,familySync:Fe,isNonGlibcLinux:Rt,isNonGlibcLinuxSync:Nt,version:Ht,versionSync:Ut}});import Kt from"node:module";import{spawn as Xt}from"node:child_process";import{chmodSync as Ve,copyFileSync as We,mkdirSync as zt,readFileSync as qe,realpathSync as Qt,rmSync as q,statSync as Zt,writeFileSync as Je,writeSync as er}from"node:fs";import{basename as Qe,dirname as tr,join as u,sep as Ye}from"node:path";import{createInterface as rr}from"node:readline";import*as Ze from"node:sea";import{fileURLToPath as et,pathToFileURL as M}from"node:url";var at=new Set;function z(e,t){let r=Object.create(null);if(e)for(let[o,n]of Object.entries(e))r[o]=n;let s=t??at;return new Proxy(process.env,{get(o,n){if(typeof n=="string"){if(Object.hasOwn(r,n))return r[n];if(!s.has(n))return process.env[n]}},set(o,n,c){return r[n]=c,!0},has(o,n){return typeof n!="string"?!1:Object.hasOwn(r,n)?r[n]!==void 0:s.has(n)?!1:n in process.env},ownKeys(o){let n=new Set(Object.keys(process.env));for(let c of s)n.delete(c);for(let c of Object.keys(r))r[c]!==void 0?n.add(c):n.delete(c);return[...n]},getOwnPropertyDescriptor(o,n){if(typeof n!="string")return;let c;if(Object.hasOwn(r,n))c=r[n];else{if(s.has(n))return;c=process.env[n]}if(c!==void 0)return{value:c,writable:!0,enumerable:!0,configurable:!0}},deleteProperty(o,n){return r[n]=void 0,!0}})}import{existsSync as ut}from"node:fs";import{basename as lt,resolve as dt}from"node:path";var Q="extension_bootstrap.mjs";function Z(e,t,r=ut){let s=e.find(c=>lt(c)===Q);if(!s)return;process.stderr.write(`[extension-fork] resolveBootstrapPath: __dir=${t}, argv-bootstrap=${s} | ||
| `);let o=dt(t,"preloads",Q),n=r(o);if(process.stderr.write(`[extension-fork] resolveBootstrapPath: localBootstrap=${o}, localExists=${n} | ||
| `),n)return o}var ft=new Set(["--server","--headless","--acp"]),pt=new Set(["completion","help","init","login","mcp","plugin","update","version"]);function mt(e){return e==="--prompt"||e.startsWith("--prompt=")||e==="-p"||e.startsWith("-p")&&e.length>2}function gt(e){if(e.some(r=>ft.has(r)||mt(r)))return!0;let t=e.find(r=>!r.startsWith("-"));return t!==void 0&&pt.has(t)}function ee(e){return gt(e)}import{readdir as ht,access as yt,constants as bt}from"node:fs/promises";import{join as f,basename as te}from"node:path";import{homedir as R}from"node:os";function ne(){return process.env.XDG_CACHE_HOME||f(R(),".cache")}function oe(){if(process.argv.includes("--no-auto-update")||process.argv.includes("--prefer-version"))return!1;let e=process.env.COPILOT_AUTO_UPDATE;return!(e&&e.toLowerCase()==="false")}function se(){let e=process.argv.indexOf("--prefer-version");if(!(e===-1||e+1>=process.argv.length))return process.argv[e+1]}function vt(){if(process.platform==="darwin")return f(R(),"Library","Caches","copilot");if(process.platform==="win32"){let e=process.env.LOCALAPPDATA||f(R(),".cache");return f(e,"copilot")}return f(ne(),"copilot")}function ie(){let e=[];return process.env.COPILOT_CACHE_HOME&&e.push(f(process.env.COPILOT_CACHE_HOME,"pkg")),e.push(f(vt(),"pkg")),e.push(f(ne(),"copilot","pkg")),process.env.COPILOT_HOME&&e.push(f(process.env.COPILOT_HOME,"pkg")),e.push(f(R(),".copilot","pkg")),[...new Set(e)]}function re(e){let t=e.match(/^(\d+)\.(\d+)\.(\d+)/);if(t)return[Number(t[1]),Number(t[2]),Number(t[3])]}function St(e,t){let r=re(e),s=re(t);if(!r&&!s)return 0;if(!r)return-1;if(!s)return 1;for(let c=0;c<3;c++)if(r[c]!==s[c])return r[c]-s[c];let o=e.includes("-"),n=t.includes("-");return o!==n?o?-1:1:e.localeCompare(t)}async function ce(e,...t){let r=[];for(let s of t){let o;try{o=await ht(s)}catch{continue}for(let n of o){let c=f(s,n);try{await yt(f(c,e),bt.R_OK),r.push(c)}catch{continue}}}return r.sort((s,o)=>{let n=St(te(o),te(s));return n!==0?n:s.localeCompare(o)}),r}import je from"node:path";import{fileURLToPath as Yt}from"node:url";function Et(e){if(e.includes("<!DOCTYPE")||e.includes("<html")){let t=Math.min(e.indexOf("<!DOCTYPE")!==-1?e.indexOf("<!DOCTYPE"):1/0,e.indexOf("<html")!==-1?e.indexOf("<html"):1/0),r=e.substring(0,t).trim();return r?`${r} [HTML error page omitted]`:"[HTML error page omitted]"}return e}function ae(e){let t;if(e instanceof Error)t=String(e);else if(typeof e=="object"&&e!==null)try{t=JSON.stringify(e)??"[object]"}catch{return"[object with circular reference]"}else t=String(e);return Et(t)}import{createRequire as Mt}from"node:module";import{platform as Dt,type as jt}from"node:os";import{join as $e,resolve as Gt}from"node:path";import{fileURLToPath as Bt}from"node:url";var H=ct(Ne(),1);function U(e={}){return(e.platform??process.platform)!=="linux"?"gnu":(e.detectLibcFamily??H.familySync)()===H.MUSL?"musl":"gnu"}function Ae(e=process.platform,t){let r=t??(e==="linux"?U():"gnu");return e==="linux"&&r==="musl"?"linuxmusl":e}function Vt(){let e=Ue(),t=e==="linux"?U({platform:e}):"gnu";return`${Ae(e,t)}-${process.arch}`}function Wt(){let e=Ue(),{arch:t}=process;switch(e){case"win32":return`win32-${t}-msvc`;case"darwin":return`darwin-${t}`;case"linux":return`linux-${t}-${U({platform:e})}`;default:throw new Error(`Unsupported platform: ${e}/${t}`)}}var p;function Ue(){if(p!==void 0)return p;switch(jt()){case"Windows_NT":p="win32";break;case"Darwin":p="darwin";break;case"Linux":p="linux";break;case"AIX":p="aix";break;case"FreeBSD":case"DragonFly":p="freebsd";break;case"OpenBSD":p="openbsd";break;case"NetBSD":p="netbsd";break;case"SunOS":p="sunos";break;default:p=Dt();break}return p}function Me(e,t){let r=Vt(),s=`${e}.node`,o=`${e}.${Wt()}.node`,n=[];for(let d of t){let O=Gt(d),C=$e(O,"prebuilds",r,s),w=He(C);if(w.ok)return w.value;n.push({path:C,err:w.err});let i=$e(O,o),a=He(i);if(a.ok)return a.value;n.push({path:i,err:a.err})}let c=n.map(d=>` ${d.path}: ${qt(d.err)}`).join(` | ||
| `);throw new Error(`Native addon "${e}" not found for ${r}. Tried: | ||
| ${c}`)}function qt(e){if(e instanceof Error)return e.message;if(typeof e=="string")return e;try{return JSON.stringify(e)}catch{return Object.prototype.toString.call(e)}}function He(e){try{return{ok:!0,value:Jt(e)}}catch(t){return{ok:!1,err:t}}}function Jt(e){return Mt(Bt(import.meta.url))(e)}var F,De=je.dirname(Yt(import.meta.url));function Ge(){if(F){if(F.kind==="ok")return F.addon;throw F.error}try{let e=Me("cli-native",[De,je.resolve(De,"..","native","cli")]);return F={kind:"ok",addon:e},e}catch(e){let t=e instanceof Error?e:new Error(`Failed to load cli-native addon: ${ae(e)}`);throw F={kind:"error",error:t},t}}function Be(){if(process.platform==="win32")return Ge()}try{Kt.enableCompileCache?.()}catch{}var nr=75;function Ke(){if(process.stdin.isTTY&&typeof process.stdin.setRawMode=="function")try{process.stdin.setRawMode(!1)}catch{}if(process.stdout.isTTY)try{er(1,"\x1B[<u\x1B[?1049h\x1B[?1049l\x1B[?1006l\x1B[?1003l\x1B[?1002l\x1B[?2004l\x1B[?1004l\x1B[?25h\x1B[?2026l"+(process.env.TERM!=="dumb"?"\x1B[23;2t":""))}catch{}}var J=tr(et(import.meta.url)),Xe=Ze.isSea();process.report.reportOnFatalError=!0;process.report.excludeEnv=!0;if(process.platform==="win32")try{let e=Be();if(!e)throw new Error("loadWin32NativeAddon returned undefined on win32");e.enableCrashReporting(),e.installExceptionFilter()}catch{}var D=process.argv.find(e=>Qe(e).startsWith("conpty_console_list_agent")),ze=Z(process.argv,J);if(D){let e=D.endsWith(".js")?D:D+".js";await import(M(e).href)}else if(ze)await import(M(ze).href);else if(process.env.COPILOT_VOICE_SERVER_MODE==="1"){let e=u(J,"voice-server.js");try{let{runVoiceServer:t}=await import(M(e).href);await t()}catch(t){process.stderr.write(`voice server: fatal at ${e}: ${t.stack??String(t)} | ||
| `),process.exit(1)}}else if(process.env.COPILOT_SHUTDOWN_FLUSH){try{let{url:e,headers:t,body:r}=JSON.parse(process.env.COPILOT_SHUTDOWN_FLUSH);await fetch(e,{method:"POST",headers:t,body:r,signal:AbortSignal.timeout(3e4)})}catch{}process.exit(0)}else if(process.env.COPILOT_RUN_APP==="1"||ee(process.argv.slice(2))){let e=u(J,"app.js"),t=se();if(Xe&&(oe()||t)){let r=ie().flatMap(o=>[u(o,"universal"),u(o,`${process.platform}-${process.arch}`)]),s=await ce("app.js",...r);if(t){let o=s.find(n=>Qe(n)===t);o?e=u(o,"app.js"):process.stderr.write(`Warning: preferred version ${t} not found in package cache, using built-in version | ||
| `)}else s.length>0&&(e=u(s[0],"app.js"))}await import(M(e).href)}else{let t=function(){let i=process.env.COPILOT_HOME||u(process.env.HOME||process.env.USERPROFILE||"",".copilot");return u(i,"restart",`${e}.json`)},r=function(){let i=t();try{let a=qe(i,"utf-8");return q(i,{force:!0}),JSON.parse(a)}catch{return}},s=function(){try{q(t(),{force:!0})}catch{}},o=function(){let i=process.env.COPILOT_HOME||u(process.env.HOME||process.env.USERPROFILE||"",".copilot");return u(i,"crash-context",`${e}.json`)},n=function(){try{let i=qe(o(),"utf-8");return JSON.parse(i)}catch{return}},c=function(){try{q(o(),{force:!0})}catch{}},d=function(i,a){return new Promise(h=>{let m=rr({input:process.stdin,output:process.stdout}),l=i.toString(16).toUpperCase();if(process.stdout.write(` | ||
| *** Copilot exited unexpectedly (exit code ${i} = 0x${l}) *** | ||
| `),a){let S=a.length>200?a.slice(0,200)+"\u2026":a;process.stdout.write(`Error: ${S} | ||
| `)}process.stdout.write(` | ||
| A crash report can be saved locally. | ||
| It will include session events, process logs, and error details. | ||
| The report may contain file paths and conversation context. | ||
| `),m.question("Save crash report? [y/N] ",S=>{m.close(),h(S.trim().toLowerCase()==="y")})})},O=function(i,a){try{let h=Qt(i);return h.startsWith(a+Ye)?Zt(h).isFile():!1}catch{return!1}},w=function(i,a){let h=Xe?i:[...process.execArgv,et(import.meta.url),...i],m=Xt(process.execPath,h,{stdio:"inherit",cwd:a,env:z({COPILOT_RUN_APP:"1",COPILOT_LOADER_PID:e})});process.env.COPILOT_LOADER_DEBUG&&process.stderr.write(`[loader] spawned app child: pid=${m.pid} (loader pid=${e}) | ||
| `);let l=g=>{try{m.kill(g)}catch{}},S=()=>l("SIGINT"),E=()=>l("SIGTERM"),j=()=>l("SIGHUP"),G=()=>l("SIGQUIT");process.on("SIGINT",S),process.on("SIGTERM",E),process.on("SIGHUP",j),process.on("SIGQUIT",G),m.on("error",g=>{process.off("SIGINT",S),process.off("SIGTERM",E),process.off("SIGHUP",j),process.off("SIGQUIT",G),s(),process.stderr.write(`Failed to start: ${g.message} | ||
| `),process.exit(1)}),m.on("exit",(g,Y)=>{if(process.off("SIGINT",S),process.off("SIGTERM",E),process.off("SIGHUP",j),process.off("SIGQUIT",G),g===nr){let x=r();if(x){let B=[...x.argv,"--session-id",x.sessionId];w(B,x.cwd||a)}else w(i,a);return}if(s(),Y)c(),Ke(),process.kill(process.pid,Y);else if(g!==0){Ke();let x=n();x&&process.stdin.isTTY?d(g??1,x.lastError).then(async B=>{B&&await C(x,g??1),c(),process.exit(g??1)}).catch(()=>{c(),process.exit(g??1)}):(c(),process.exit(g??1))}else c(),process.exit(0)})};or=t,sr=r,ir=s,cr=o,ar=n,ur=c,lr=d,dr=O,fr=w;let e=String(process.pid);async function C(i,a){try{let h=new Date().toISOString().replace(/[:.]/g,"-"),m=process.env.COPILOT_HOME||u(process.env.HOME||process.env.USERPROFILE||"",".copilot"),l=u(m,"crash-reports",`crash-${i.sessionId}-${h}`);zt(l,{recursive:!0,mode:448}),Je(u(l,"crash-context.json"),JSON.stringify(i,null,2),{mode:384});let S=["# Crash Report","",`**Exit code**: ${a}`,i.errorType?`**Error type**: ${i.errorType}`:"",i.lastError?`**Error**: ${i.lastError}`:"",`**Timestamp**: ${new Date().toISOString()}`,`**Session ID**: ${i.sessionId}`,`**Working directory**: ${i.cwd}`,i.stackTrace?` | ||
| ## Stack Trace | ||
| \`\`\` | ||
| ${i.stackTrace} | ||
| \`\`\``:"",""].filter(Boolean).join(` | ||
| `);if(Je(u(l,"feedback.md"),S,{mode:384}),i.sessionFilePath&&O(i.sessionFilePath,m))try{let E=u(l,"events.jsonl");We(i.sessionFilePath,E),Ve(E,384)}catch{}if(i.logFilePath&&O(i.logFilePath,m))try{let E=u(l,"process.log");We(i.logFilePath,E),Ve(E,384)}catch{}process.stdout.write(` | ||
| Crash report saved to: ${l}${Ye} | ||
| `)}catch(h){process.stderr.write(`Failed to create crash report: ${String(h)} | ||
| `)}}w(process.argv.slice(2),process.cwd())}var or,sr,ir,cr,ar,ur,lr,dr,fr; |
Sorry, the diff of this file is not supported yet
| {"Source":"InternalBuild","Data":{"System.CollectionId":"cb55739e-4afe-46a3-970f-1b49d8ee7564","System.DefinitionId":"190018","System.TeamProjectId":"c93e867a-8815-43c1-92c4-e7dd5404f1e1","System.TeamProject":"Dart","Build.BuildId":"148237282","Build.BuildNumber":"v0.6.0_20260530.1","Build.DefinitionName":"MXC-Official-Build","Build.DefinitionRevision":"7","Build.Repository.Name":"microsoft/mxc","Build.Repository.Provider":"GitHub","Build.Repository.Id":"microsoft/mxc","Build.SourceBranch":"refs/tags/v0.6.0","Build.SourceBranchName":"v0.6.0","Build.SourceVersion":"206dd5475c80b127fc92a4a38d49f83e77d054d5","Build.Repository.Uri":"https://github.com/microsoft/mxc","1ES.PT.TemplateType":"official"},"Feed":null} |
| {"StdOut":"******************************************************************************\r\nMachine Information\r\nMachine Name: at-blueKVNUEK\r\nMachine Ip: 10.0.0.31\r\nOperating System: Microsoft Windows NT 10.0.26100.0\r\nUser Name: at-blueKVNUEK$\r\nProcessor Count: 64\r\nProcess Name: EsrpClient\r\nProcess Id: 4348\r\nCaller Program: EsrpClient.exe\r\nIdentity: NT AUTHORITY\\NETWORK SERVICE\r\nProcess Version: 1.2.142+Branch.master.Sha.90404b43284ec55b3e2251d0e272f8932aa583e2\r\nProcess Bitness: 64 bit\r\n******************************************************************************\r\nCommandline received: Sign | -a | c:\\Temp\\AzureTemp\\TFSTemp\\vctmp7008_89278.json | -p | c:\\Temp\\AzureTemp\\TFSTemp\\vctmp7008_834052.json | -c | c:\\Temp\\AzureTemp\\TFSTemp\\vctmp7008_982164.json | -i | c:\\Temp\\AzureTemp\\TFSTemp\\vctmp15908_908389.json | -o | c:\\Temp\\AzureTemp\\TFSTemp\\v583C32.tmp | -l | Verbose ESRP session Id is: 8fbe2d3f-d5a4-4719-8272-b639dba3eace\r\n2026-05-30T07:12:00.5158379Z:Command you are trying to use is: Sign\r\n2026-05-30T07:12:00.5225025Z:Correlation Vector for this run is: 282cb55f-4224-4b82-aadd-52f662cc5d1b\r\n2026-05-30T07:12:00.5225025Z:Groupid for this run is empty\r\n2026-05-30T07:12:00.6419064Z:request signing cert being used is this thumbprint: 42661F29AF78973B30992C76D622575F555E3D84 in store LocalMachine\\My\r\n2026-05-30T07:12:00.6419064Z:Both certificates validation passed.\r\n2026-05-30T07:12:00.6438051Z:The auth cert we choose to use, subject name is: CN=0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f.microsoft.com, thumbprint is: 943507930F1390CF64FC5E9DB45F03091556EEBE\r\n2026-05-30T07:12:00.7082902Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:00Z] ConfidentialClientApplication 37368736 created\r\n2026-05-30T07:12:00.7082902Z:Gateway Client: Enter AuthenticationProvider, no calls to AAD yet, client id is 0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f, and resource url is https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com, tenant id is 33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:00.7201671Z:There is no memory mapped file accociated with this name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:00.7258506Z:AAD auth caching is in use with this name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:00.7392617Z:Validate and Renew Token if necessary finished: 00:00:00.0095422\r\n2026-05-30T07:12:00.8433612Z:Session request is: {\r\n \"expiresAfter\": \"3.00:00:00\",\r\n \"partitionCount\": 0,\r\n \"isProvisionStorage\": true,\r\n \"isLegacyCopsModel\": false,\r\n \"commandName\": \"Sign\",\r\n \"intent\": \"digestsign\",\r\n \"contentType\": \"Bin\",\r\n \"contentOrigin\": \"3rd Party\",\r\n \"productState\": null,\r\n \"audience\": \"External Broad\"\r\n}\r\n2026-05-30T07:12:00.8928927Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:00Z] ConfidentialClientApplication 29578451 created\r\n2026-05-30T07:12:00.8928927Z:Gateway Client: Enter AuthenticationProvider, no calls to AAD yet, client id is 0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f, and resource url is https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com, tenant id is 33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:00.8998062Z:Gateway Client: a new client and client id is created: cf717689-dc5e-42f5-8b73-ac1695e6d260\r\n2026-05-30T07:12:00.9392670Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:00Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] MSAL MSAL.Desktop with assembly version '4.70.0.0'. CorrelationId(c112b9e6-b8d2-407a-ab29-9b45bd0c4dda)\r\n2026-05-30T07:12:00.9543183Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:00Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] === AcquireTokenForClientParameters ===\r\nSendX5C: True\r\nForceRefresh: False\r\nAccessTokenHashToRefresh: False\r\n\r\n2026-05-30T07:12:00.9675620Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:00Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] \r\n=== Request Data ===\r\nAuthority Provided? - True\r\nScopes - https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com/.default\r\nExtra Query Params Keys (space separated) - \r\nApiId - AcquireTokenForClient\r\nIsConfidentialClient - True\r\nSendX5C - True\r\nLoginHint ? False\r\nIsBrokerConfigured - False\r\nHomeAccountId - False\r\nCorrelationId - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda\r\nUserAssertion set: False\r\nLongRunningOboCacheKey set: False\r\nRegion configured: \r\n\r\n2026-05-30T07:12:00.9685447Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:00Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] === Token Acquisition (ClientCredentialRequest) started:\r\n\t Scopes: https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com/.default\r\n\tAuthority Host: login.microsoftonline.com\r\n2026-05-30T07:12:00.9866660Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:00Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] [Internal cache] Total number of cache partitions found while getting access tokens: 0\r\n2026-05-30T07:12:00.9866660Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:00Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] [FindAccessTokenAsync] Discovered 0 access tokens in cache using partition key: 0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d_AppTokenCache\r\n2026-05-30T07:12:00.9866660Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:00Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] [FindAccessTokenAsync] No access tokens found in the cache. Skipping filtering. \r\n2026-05-30T07:12:00.9968516Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:00Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] [Instance Discovery] Instance discovery is enabled and will be performed\r\n2026-05-30T07:12:01.0023755Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:01Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] [Region discovery] WithAzureRegion not configured. \r\n2026-05-30T07:12:01.0023755Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:01Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] [Region discovery] Not using a regional authority. \r\n2026-05-30T07:12:01.0063296Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:01Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] [Instance Discovery] Tried to use network cache provider for login.microsoftonline.com. Success? False. \r\n2026-05-30T07:12:01.0181913Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:01Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] Fetching instance discovery from the network from host login.microsoftonline.com. \r\n2026-05-30T07:12:01.0359433Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:01Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] Starting [Oauth2Client] Sending GET request \r\n2026-05-30T07:12:01.0399526Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:01Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] Starting [HttpManager] ExecuteAsync\r\n2026-05-30T07:12:01.0479597Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:01Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] [HttpManager] Sending request. Method: GET. Host: https://login.microsoftonline.com. Binding Certificate: False \r\n2026-05-30T07:12:01.7398775Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:01Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] [HttpManager] Received response. Status code: OK. \r\n2026-05-30T07:12:01.7534277Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:01Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] Finished [HttpManager] ExecuteAsync in 712 ms\r\n2026-05-30T07:12:01.7534277Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:01Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] Finished [Oauth2Client] Sending GET request in 716 ms\r\n2026-05-30T07:12:01.7656128Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:01Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] Starting [OAuth2Client] Deserializing response\r\n2026-05-30T07:12:01.9295701Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:01Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] Finished [OAuth2Client] Deserializing response in 164 ms\r\n2026-05-30T07:12:01.9302009Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:01Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] [Instance Discovery] Tried to use network cache provider for login.microsoftonline.com. Success? True. \r\n2026-05-30T07:12:01.9302009Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:01Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] [Instance Discovery] After hitting the discovery endpoint, the network provider found an entry for login.microsoftonline.com ? True. \r\n2026-05-30T07:12:01.9357840Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:01Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] Authority validation enabled? True. \r\n2026-05-30T07:12:01.9360968Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:01Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] Authority validation - is known env? True. \r\n2026-05-30T07:12:01.9555004Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:01Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] Starting TokenClient:SendTokenRequestAsync\r\n2026-05-30T07:12:01.9596965Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:01Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] [TokenClient] Before adding the client assertion / secret\r\n2026-05-30T07:12:01.9610760Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:01Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] Building assertion from certificate with clientId: 0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f at endpoint: https://login.microsoftonline.com/33e01921-4d64-4f8c-a055-5bdaffd5e33d/oauth2/v2.0/token\r\n2026-05-30T07:12:01.9610760Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:01Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] Proceeding with JWT token creation and adding client assertion.\r\n2026-05-30T07:12:01.9996048Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:01Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] [TokenClient] After adding the client assertion / secret\r\n2026-05-30T07:12:02.0106001Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:02Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] [Token Client] Fetching MsalTokenResponse .... \r\n2026-05-30T07:12:02.0106001Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:02Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] Starting [Oauth2Client] Sending POST request \r\n2026-05-30T07:12:02.0178325Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:02Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] Starting [HttpManager] ExecuteAsync\r\n2026-05-30T07:12:02.0178325Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:02Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] [HttpManager] Sending request. Method: POST. Host: https://login.microsoftonline.com. Binding Certificate: False \r\n2026-05-30T07:12:02.2475813Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:02Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] [HttpManager] Received response. Status code: OK. \r\n2026-05-30T07:12:02.2475813Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:02Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] Finished [HttpManager] ExecuteAsync in 230 ms\r\n2026-05-30T07:12:02.2475813Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:02Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] Finished [Oauth2Client] Sending POST request in 237 ms\r\n2026-05-30T07:12:02.2475813Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:02Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] Starting [OAuth2Client] Deserializing response\r\n2026-05-30T07:12:02.2650187Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:02Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] Finished [OAuth2Client] Deserializing response in 18 ms\r\n2026-05-30T07:12:02.2650187Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:02Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] ScopeSet was missing from the token response, so using developer provided scopes in the result. \r\n2026-05-30T07:12:02.2650187Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:02Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] Finished TokenClient:SendTokenRequestAsync in 311 ms\r\n2026-05-30T07:12:02.2687123Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:02Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] Checking client info returned from the server..\r\n2026-05-30T07:12:02.2687123Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:02Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] Saving token response to cache..\r\n2026-05-30T07:12:02.2802849Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:02Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] \r\n[MsalTokenResponse]\r\nError: \r\nErrorDescription: \r\nScopes: https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com/.default\r\nExpiresIn: 86399\r\nRefreshIn: 43199\r\nAccessToken returned: True\r\nAccessToken Type: Bearer\r\nRefreshToken returned: False\r\nIdToken returned: False\r\nClientInfo returned: False\r\nFamilyId: \r\nWamAccountId exists: False\r\n2026-05-30T07:12:02.2810579Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:02Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] [SaveTokenResponseAsync] ID Token not present in response. \r\n2026-05-30T07:12:02.2822927Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:02Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] Cannot determine home account ID - or id token or no client info and no subject \r\n2026-05-30T07:12:02.2887352Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:02Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] [SaveTokenResponseAsync] Entering token cache semaphore. Count Real semaphore: True. Count: 1.\r\n2026-05-30T07:12:02.2890873Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:02Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] [SaveTokenResponseAsync] Entered token cache semaphore. \r\n2026-05-30T07:12:02.2890873Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:02Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] [SaveTokenResponseAsync] Saving AT in cache and removing overlapping ATs...\r\n2026-05-30T07:12:02.2915474Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:02Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] Looking for scopes for the authority in the cache which intersect with https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com/.default\r\n2026-05-30T07:12:02.2915474Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:02Z] [Internal cache] Total number of cache partitions found while getting access tokens: 0\r\n2026-05-30T07:12:02.2915474Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:02Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] Intersecting scope entries count - 0\r\n2026-05-30T07:12:02.2937825Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:02Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] Not saving to ADAL legacy cache. \r\n2026-05-30T07:12:02.2937825Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:02Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] [SaveTokenResponseAsync] Released token cache semaphore. \r\n2026-05-30T07:12:02.2975899Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:02Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] \r\n\t=== Token Acquisition finished successfully:\r\n2026-05-30T07:12:02.2986424Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:02Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] AT expiration time: 5/31/2026 7:12:01 AM +00:00, scopes: https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com/.default. source: IdentityProvider\r\n2026-05-30T07:12:02.2991523Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:02Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] Fetched access token from host login.microsoftonline.com. \r\n2026-05-30T07:12:02.3007431Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:02Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] \r\n[LogMetricsFromAuthResult] Cache Refresh Reason: NoCachedAccessToken\r\n[LogMetricsFromAuthResult] DurationInCacheInMs: 0\r\n[LogMetricsFromAuthResult] DurationTotalInMs: 1339\r\n[LogMetricsFromAuthResult] DurationInHttpInMs: 919\r\n2026-05-30T07:12:02.3007431Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:02Z - c112b9e6-b8d2-407a-ab29-9b45bd0c4dda] TokenEndpoint: ****\r\n2026-05-30T07:12:02.3166098Z:Use AAD token. CERT\r\n2026-05-30T07:12:02.3285375Z:New Token Acquisition Time: 00:00:01.4181303\r\n2026-05-30T07:12:02.3707538Z:Gateway Client: the client id that is sending a request is: cf717689-dc5e-42f5-8b73-ac1695e6d260\r\n2026-05-30T07:12:03.0035979Z:Gateway Client: response send time from cloud gateway: 2026-05-30T07:12:02.9413463+00:00\r\n2026-05-30T07:12:03.0035979Z:Gateway Client: response receive time from client SendAsync: 2026-05-30T07:12:03.0035979+00:00\r\n2026-05-30T07:12:03.0035979Z:Gateway Client: response duration time between cloud gateway and client SendAsync: 00:00:00.0622516\r\n2026-05-30T07:12:03.1206664Z:Session request requestid: adb0ca81-5eef-47cf-8363-ba4b7173d294, and submission status is: Pass\r\n2026-05-30T07:12:03.1814375Z:Provision storage complete. Total shards: 100\r\n2026-05-30T07:12:03.1855507Z:Loading DigestSignErrorMappingCache mapping info\r\n2026-05-30T07:12:03.1950074Z:DigestSignErrorMappingCache from server, # of DigestSignOperationErrorPatterns object we get is :: \r\n2026-05-30T07:12:03.2355923Z:Consolidate DigestSignErrorMappingCache, # of DigestSignOperationErrorPatterns object we get is :: 34\r\n2026-05-30T07:12:03.2355923Z:Successfully retrieved policy: policy is {\"policy\":{\"id\":\"0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f-0\",\"workflowExecutionType\":3}}\r\n2026-05-30T07:12:03.2465858Z:Successfully get telemetry connection string: Endpo......\r\n2026-05-30T07:12:03.2472232Z:Warning: \r\n2026-05-30T07:12:03.2512359Z:IAuthInfo constructed: {\r\n \"authenticationType\": \"AAD_CERT\",\r\n \"clientId\": \"0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f\",\r\n \"tenantId\": \"33e01921-4d64-4f8c-a055-5bdaffd5e33d\",\r\n \"aadAuthorityBaseUri\": \"https://login.microsoftonline.com/\",\r\n \"authCert\": {\r\n \"storeLocation\": \"LocalMachine\",\r\n \"storeName\": \"My\",\r\n \"subjectName\": \"CN=0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f.microsoft.com\",\r\n \"sendX5c\": true,\r\n \"withAzureRegion\": false,\r\n \"getCertFromKeyVault\": false,\r\n \"keyVaultName\": null,\r\n \"keyVaultCertName\": null\r\n },\r\n \"requestSigningCert\": {\r\n \"storeLocation\": \"LocalMachine\",\r\n \"storeName\": \"My\",\r\n \"subjectName\": \"CN=0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f\",\r\n \"sendX5c\": false,\r\n \"withAzureRegion\": false,\r\n \"getCertFromKeyVault\": false,\r\n \"keyVaultName\": null,\r\n \"keyVaultCertName\": null\r\n },\r\n \"oAuthToken\": null,\r\n \"version\": \"1.0.0\",\r\n \"esrpClientId\": \"0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f\",\r\n \"federatedTokenData\": null,\r\n \"federatedTokenPath\": null\r\n}\r\n2026-05-30T07:12:03.2532464Z:IPolicyInfo constructed: {\r\n \"intent\": \"digestsign\",\r\n \"contentType\": \"Bin\",\r\n \"contentOrigin\": \"3rd Party\",\r\n \"productState\": null,\r\n \"audience\": \"External Broad\",\r\n \"version\": \"1.0.0\"\r\n}\r\n2026-05-30T07:12:03.2562149Z:IConfigInfo constructed: {\r\n \"esrpApiBaseUri\": \"https://api.esrp.microsoft.com/api/v2/\",\r\n \"esrpSessionTimeoutInSec\": 60,\r\n \"minThreadPoolThreads\": 256,\r\n \"maxDegreeOfParallelism\": 256,\r\n \"exponentialFirstFastRetry\": true,\r\n \"exponentialRetryCount\": 2,\r\n \"exponentialRetryDeltaBackOff\": \"00:00:05\",\r\n \"exponentialRetryMaxBackOff\": \"00:01:00\",\r\n \"exponentialRetryMinBackOff\": \"00:00:03\",\r\n \"appDataFolder\": \"C:\\\\Windows\\\\ServiceProfiles\\\\NetworkService\\\\AppData\\\\Local\",\r\n \"certificateCacheFolder\": null,\r\n \"version\": \"1.0.0\",\r\n \"exitOnFlaggedFile\": false,\r\n \"flaggedFileClientWaitTimeout\": \"1.00:00:00\",\r\n \"servicePointManagerDefaultConnectionLimit\": 256,\r\n \"isOnPremGateway\": false,\r\n \"diagnosticListeners\": null,\r\n \"securityProtocolType\": \"Tls12\",\r\n \"parallelOperationsInFileUploadDownload\": 300,\r\n \"maxTelemetryBuffer\": 200000,\r\n \"telemetryTimeoutInSec\": 0,\r\n \"resourceUri\": \"https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com\",\r\n \"cacheRootFolder\": null,\r\n \"cachedFileTTLInMin\": 7200\r\n}\r\n2026-05-30T07:12:03.5175476Z:Client Telemetry: Xpert agent is not running on the machine. Using events hub for processing client telemetry.\r\n2026-05-30T07:12:03.7274495Z:Key: TotalSignOperationDataCount, Value: 1\r\n2026-05-30T07:12:03.7447412Z:Start Time:5/30/2026 7:12:03 AM, Starting the sign workflow...\r\n2026-05-30T07:12:03.7592683Z:some input info: {\r\n \"contextData\": {\r\n \"build_buildnumber\": \"AzureDevOps_M273_20260511.16\",\r\n \"esrpClientVersion\": \"1.2.142+Branch.master.Sha.90404b43284ec55b3e2251d0e272f8932aa583e2\",\r\n \"userIpAddress\": \"10.0.0.31\",\r\n \"userAgent\": \"at-blueKVNUEK\"\r\n },\r\n \"sourceDirectory\": null,\r\n \"sourceLocation\": \"c:\\\\Temp\\\\AzureTemp\\\\pqgjedog.0s5\\\\manifest.cat\",\r\n \"destinationDirectory\": null,\r\n \"destinationLocation\": \"c:\\\\Temp\\\\AzureTemp\\\\pqgjedog.0s5\\\\manifest.cat\",\r\n \"sizeInBytes\": 0,\r\n \"name\": \"manifest.cat\",\r\n \"isSuccess\": null,\r\n \"operationStartedAt\": \"0001-01-01T00:00:00+00:00\",\r\n \"operationEndedAt\": \"0001-01-01T00:00:00+00:00\",\r\n \"operationDurationMS\": 0,\r\n \"processName\": \"EsrpClient\",\r\n \"processId\": 4348,\r\n \"processVersion\": \"1.2.142+Branch.master.Sha.90404b43284ec55b3e2251d0e272f8932aa583e2\",\r\n \"customerCorrelationId\": null,\r\n \"esrpClientSessionGuid\": \"8fbe2d3f-d5a4-4719-8272-b639dba3eace\",\r\n \"callerProgram\": \"EsrpClient\",\r\n \"indentity\": \"NT AUTHORITY\\\\NETWORK SERVICE\",\r\n \"clientId\": \"0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f\",\r\n \"exceptionData\": null,\r\n \"apiBaseUrl\": \"https://api.esrp.microsoft.com/api/v2/\",\r\n \"handlerWorkflow\": \"Sign\",\r\n \"submissionRequest\": {\r\n \"contextData\": {\r\n \"build_buildnumber\": \"AzureDevOps_M273_20260511.16\",\r\n \"esrpClientVersion\": \"1.2.142+Branch.master.Sha.90404b43284ec55b3e2251d0e272f8932aa583e2\",\r\n \"userIpAddress\": \"10.0.0.31\",\r\n \"userAgent\": \"at-blueKVNUEK\"\r\n },\r\n \"groupId\": null,\r\n \"correlationVector\": \"282cb55f-4224-4b82-aadd-52f662cc5d1b\",\r\n \"driEmail\": null,\r\n \"version\": \"1.0.0\"\r\n },\r\n \"policyInfo\": {\r\n \"intent\": \"digestsign\",\r\n \"contentType\": \"Bin\",\r\n \"contentOrigin\": \"3rd Party\",\r\n \"productState\": null,\r\n \"audience\": \"External Broad\",\r\n \"version\": \"1.0.0\"\r\n },\r\n \"osVersion\": \"Microsoft Windows NT 10.0.26100.0\",\r\n \"executingMachineIPAddress\": \"10.0.0.31\"\r\n}\r\n2026-05-30T07:12:03.8063499Z:Executing SignWorkflowType is DigestSignStaticAzure\r\n2026-05-30T07:12:03.8235528Z:Source file \"c:\\Temp\\AzureTemp\\pqgjedog.0s5\\manifest.cat\" hashed in 3 ms\r\n2026-05-30T07:12:03.8711619Z:Thread: 1 - Certificate file mutex \"26JMnLfe+lJgLQbZlInxxRapH+R6+rY1QYJbI4pAL78=_CP-464321\" acquired (waited 0 ms).\r\nMSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:03Z] ConfidentialClientApplication 44123454 created\r\n2026-05-30T07:12:03.8840731Z:Gateway Client: Enter AuthenticationProvider, no calls to AAD yet, client id is 0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f, and resource url is https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com, tenant id is 33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:03.8847876Z:Cached token found with name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:03.8854347Z:AAD auth caching is in use with this name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:03.9780323Z:Global cached token is valid from 5/30/2026 7:07:02 AM to 5/31/2026 7:12:02 AM (UTC). Total validity from current time is 86398.0232041 seconds\r\n2026-05-30T07:12:03.9780323Z:Validate and Renew Token if necessary finished: 00:00:00.0926516\r\nMSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:03Z] ConfidentialClientApplication 20852350 created\r\n2026-05-30T07:12:03.9780323Z:Gateway Client: Enter AuthenticationProvider, no calls to AAD yet, client id is 0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f, and resource url is https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com, tenant id is 33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:03.9780323Z:Gateway Client: a new client and client id is created: 7b47a4c9-515f-4057-921a-f7423f502a63\r\n2026-05-30T07:12:03.9780323Z:5/30/2026 7:12:03 AM +00:00:Digest Signing Factory Client type loaded is - [AzureGatewaySubmitter].\r\n2026-05-30T07:12:03.9895599Z:Existing Token Acquisition Time: 00:00:00.0004198\r\n2026-05-30T07:12:04.0681419Z:Gateway Client: the client id that is sending a request is: 7b47a4c9-515f-4057-921a-f7423f502a63\r\n2026-05-30T07:12:04.2501094Z:Gateway Client: response send time from cloud gateway: 2026-05-30T07:12:04.2229197+00:00\r\n2026-05-30T07:12:04.2506230Z:Gateway Client: response receive time from client SendAsync: 2026-05-30T07:12:04.2506230+00:00\r\n2026-05-30T07:12:04.2506230Z:Gateway Client: response duration time between cloud gateway and client SendAsync: 00:00:00.0277033\r\n2026-05-30T07:12:04.2513909Z:Gateway Client: response time after converting from http response to object: 2026-05-30T07:12:04.2513909+00:00\r\n2026-05-30T07:12:04.6040301Z:Client Telemetry: Events published in this batch: 4\r\n2026-05-30T07:12:04.6040301Z:Client Telemetry: Events received so far in this session: 4\r\n2026-05-30T07:12:04.6040301Z:Client Telemetry: Events published so far in this session: 4\r\n2026-05-30T07:12:04.7712689Z:Existing Token Acquisition Time: 00:00:00.0008419\r\n2026-05-30T07:12:04.7749463Z:Gateway Client: the client id that is sending a request is: 7b47a4c9-515f-4057-921a-f7423f502a63\r\n2026-05-30T07:12:04.8308008Z:Gateway Client: response send time from cloud gateway: 2026-05-30T07:12:04.8166599+00:00\r\n2026-05-30T07:12:04.8308008Z:Gateway Client: response receive time from client SendAsync: 2026-05-30T07:12:04.8308008+00:00\r\n2026-05-30T07:12:04.8308008Z:Gateway Client: response duration time between cloud gateway and client SendAsync: 00:00:00.0141409\r\n2026-05-30T07:12:04.8389370Z:Gateway Client: response time after converting from http response to object: 2026-05-30T07:12:04.8389370+00:00\r\n2026-05-30T07:12:04.8437195Z:Reading thumbprint from \"C:\\Windows\\ServiceProfiles\\NetworkService\\AppData\\Local\\EsrpClient_8fbe2d3fd5a447198272b639dba3eace\\Certificates\\CP-464321.p7b\" (9692 bytes)...\r\n2026-05-30T07:12:04.8718907Z:Thread: 1 - Wrote new certificate file to \"C:\\Windows\\ServiceProfiles\\NetworkService\\AppData\\Local\\EsrpClient_8fbe2d3fd5a447198272b639dba3eace\\Certificates\\CP-464321.p7b\".\r\n2026-05-30T07:12:04.8718907Z:Thread: 1 - Certificate file mutex \"26JMnLfe+lJgLQbZlInxxRapH+R6+rY1QYJbI4pAL78=_CP-464321\" released (held for 1001 ms).\r\n2026-05-30T07:12:04.8735369Z:digest sign request expire time is: 5/30/2026 7:13:03 AM\r\n2026-05-30T07:12:07.7295539Z:Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe sign /NPH /fd \"SHA256\" /f \"C:\\Windows\\ServiceProfiles\\NetworkService\\AppData\\Local\\EsrpClient_8fbe2d3fd5a447198272b639dba3eace\\Certificates\\CP-464321.p7b\" /sha1 \"464FA2A9D4A43EA5C3E53CE4562B501CB6EFA47F\" /du \"https://www.1eswiki.com/wiki/ADO_Manifest_Generator\" /d \"Packaging SSSC Codesign - DigestSign\" /tr \"http://aztss.trafficmanager.net/TSS/HttpTspServer\" /td sha256 /dlib \"c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\EsrpClient.Sign.DigestSignLib.dll\" /dmdf \"C:\\Windows\\ServiceProfiles\\NetworkService\\AppData\\Local\\EsrpClient_8fbe2d3fd5a447198272b639dba3eace\\75A88B6094D34A9792C8A99B6C3A2C03.json\" \"c:\\Temp\\AzureTemp\\pqgjedog.0s5\\manifest.cat\" completed in 2855 ms\r\nExit Code: 0\r\nStdOut:\r\nDone Adding Additional Store\r\n\r\nESRP Digest Signing\r\n\r\n2026-05-30T07:12:05.8570107Z:Digest Signer : SecurityProtocolType used from the metadata info is : Tls12\r\nMSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:05Z] ConfidentialClientApplication 654897 created\r\n2026-05-30T07:12:06.0060611Z:Cached token found with name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:06.0081322Z:AAD auth caching is in use with this name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:06.1249429Z:Global cached token is valid from 5/30/2026 7:07:02 AM to 5/31/2026 7:12:02 AM (UTC). Total validity from current time is 86395.8759236 seconds\r\n2026-05-30T07:12:06.1249429Z:Validate and Renew Token if necessary finished: 00:00:00.1161941\r\nMSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:06Z] ConfidentialClientApplication 49044892 created\r\n2026-05-30T07:12:06.1978856Z:Existing Token Acquisition Time: 00:00:00.0030251\r\n2026-05-30T07:12:06.7953930Z:Gateway Submission Request Signing Time: 00:00:00.1129320\r\n2026-05-30T07:12:06.7953930Z:Gateway Submission Request Send Time: 00:00:00.4627859\r\n2026-05-30T07:12:06.7953930Z:Gateway Submission Overall Submission Time: 00:00:00.5938012\r\n2026-05-30T07:12:06.7953930Z:Operation ID: a9f774db-ca0e-48a9-a2ee-5a3bedbabad5\r\n2026-05-30T07:12:07.3036189Z:Gateway Status Delay Time: 00:00:00.5089525\r\n2026-05-30T07:12:07.3144410Z:Existing Token Acquisition Time: 00:00:00.0007334\r\n2026-05-30T07:12:07.4381074Z:Gateway Status Request Send Time: 00:00:00.1176954\r\n2026-05-30T07:12:07.4381074Z:Gateway Status Overall Time: 00:00:00.1202089\r\n2026-05-30T07:12:07.4457636Z:Gateway Submission Duration: 936\r\n2026-05-30T07:12:07.4457636Z:Gateway Submission Attempts: 1\r\n2026-05-30T07:12:07.4457636Z:Gateway GetStatus Duration: 133\r\n2026-05-30T07:12:07.4457636Z:Gateway GetStatus Attempts: 1\r\n2026-05-30T07:12:07.4457636Z:$$5a9f5111cf8f42a0a0edc419862b95dc##_ThrottleCount: 0\r\n2026-05-30T07:12:07.4457636Z:$$65265a48891640eb86e148d3c9a627fa##_ThrottledTimeInSec: 0\r\n2026-05-30T07:12:07.4457636Z:Service Call: 1727.2005 ms\r\nSuccessfully signed: c:\\Temp\\AzureTemp\\pqgjedog.0s5\\manifest.cat\r\n\r\n\r\n\r\nStdErr:\r\n\r\n\r\n\r\n\r\n2026-05-30T07:12:07.9240870Z:Warning: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\pqgjedog.0s5\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\pqgjedog.0s5\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n2026-05-30T07:12:07.9260929Z:Warning: Warning: Operation Error (1) - Operation: signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\pqgjedog.0s5\\manifest.cat\" - Retrying in 500 ms...\r\n2026-05-30T07:12:08.0745529Z:Warning: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\pqgjedog.0s5\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\pqgjedog.0s5\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n2026-05-30T07:12:08.0751129Z:Warning: Warning: Operation Error (2) - Operation: signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\pqgjedog.0s5\\manifest.cat\" - Retrying in 500 ms...\r\n2026-05-30T07:12:08.1501876Z:Client Telemetry: Events published in this batch: 3\r\n2026-05-30T07:12:08.1501876Z:Client Telemetry: Events received so far in this session: 7\r\n2026-05-30T07:12:08.1501876Z:Client Telemetry: Events published so far in this session: 7\r\n2026-05-30T07:12:08.7096102Z:Warning: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\pqgjedog.0s5\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\pqgjedog.0s5\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n2026-05-30T07:12:08.7454583Z:\r\n2026-05-30T07:12:08.7454583Z:Error: System.AggregateException: One or more errors occurred. ---> MS.Ess.EsrpClient.Sign.Exceptions.EsrpDigestSignExecuteProcessFailedException: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\pqgjedog.0s5\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\pqgjedog.0s5\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass27_0.<ExecuteDigestSignOperation>b__1()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.<>c__DisplayClass1.<ExecuteAction>b__0()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.ExecuteAction[TResult](Func`1 func)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.ExecuteDigestSignOperation(Input digestSignInput, Operation digestSignOperation, SignOperationData fileEntry, SignStatusResponse completionResponse, SignCommandDefinition param, Guid correlationId, String correlationVector, CancellationToken cancellationToken, String requestId, String tmpDestinationLocation)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.DigestSignInternal(Input digestSignInput, SignCommandDefinition param, Guid& correlationId, String correlationVector, CancellationToken cancellationToken, String groupId, SignOperationData fileEntry, Int32& signedFileCount, Int32& certRefreshCount, ConcurrentBag`1 completionResponses, RetryPolicy`1 operationRetryPolicy, String dynamicCertificateFile, String dynamicCertificateThumbprint, String requestId)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass23_3.<DigestSignAsync>b__6(SignOperationData fileEntry)\r\n --- End of inner exception stack trace ---\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<DigestSignAsync>d__23.MoveNext()\r\n---> (Inner Exception #0) MS.Ess.EsrpClient.Sign.Exceptions.EsrpDigestSignExecuteProcessFailedException: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\pqgjedog.0s5\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\pqgjedog.0s5\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass27_0.<ExecuteDigestSignOperation>b__1()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.<>c__DisplayClass1.<ExecuteAction>b__0()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.ExecuteAction[TResult](Func`1 func)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.ExecuteDigestSignOperation(Input digestSignInput, Operation digestSignOperation, SignOperationData fileEntry, SignStatusResponse completionResponse, SignCommandDefinition param, Guid correlationId, String correlationVector, CancellationToken cancellationToken, String requestId, String tmpDestinationLocation)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.DigestSignInternal(Input digestSignInput, SignCommandDefinition param, Guid& correlationId, String correlationVector, CancellationToken cancellationToken, String groupId, SignOperationData fileEntry, Int32& signedFileCount, Int32& certRefreshCount, ConcurrentBag`1 completionResponses, RetryPolicy`1 operationRetryPolicy, String dynamicCertificateFile, String dynamicCertificateThumbprint, String requestId)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass23_3.<DigestSignAsync>b__6(SignOperationData fileEntry)<---\r\n\r\n2026-05-30T07:12:08.7813860Z:Constructing EsrpClientResponse\r\n2026-05-30T07:12:08.7823262Z:EsrpClientResponse is constructed\r\n2026-05-30T07:12:08.8152146Z:OperationDurationMs: 4984\r\n2026-05-30T07:12:08.8152146Z:DynamicCertGenerationTimeMs: 0\r\n2026-05-30T07:12:08.8152146Z:TimeToGetStorageShradsMs: 0\r\n2026-05-30T07:12:08.8152146Z:TotalScanSubmissionTimeMs: 0\r\n2026-05-30T07:12:08.8152146Z:ThrottleCount: 0\r\n2026-05-30T07:12:08.8152146Z:ThrottledTimeInSec: 0\r\n2026-05-30T07:12:08.8152146Z:\r\n2026-05-30T07:12:08.8160771Z:0/1 files signed in 00:00:04.984 (0 files/s, total number of certs refreshed: 0)\r\n2026-05-30T07:12:08.8182732Z:result json is: {\r\n \"submissionResponses\": [\r\n {\r\n \"fileStatusDetail\": [\r\n {\r\n \"sourceHash\": \"l2CvdLFXmXCqDbMKKSTqsnXo6IhjNJHmfylxVFlQ1ek=\",\r\n \"hashType\": \"sha256\",\r\n \"destinationHash\": null,\r\n \"certificateThumbprint\": null,\r\n \"destinationLocation\": \"c:\\\\Temp\\\\AzureTemp\\\\pqgjedog.0s5\\\\manifest.cat\",\r\n \"destinationFileSizeInBytes\": 0,\r\n \"sourceLocation\": \"c:\\\\Temp\\\\AzureTemp\\\\pqgjedog.0s5\\\\manifest.cat\"\r\n }\r\n ],\r\n \"operationId\": \"a9f774db-ca0e-48a9-a2ee-5a3bedbabad5\",\r\n \"customerCorrelationId\": \"282cb55f-4224-4b82-aadd-52f662cc5d1b\",\r\n \"statusCode\": \"failCanRetry\",\r\n \"errorInfo\": {\r\n \"code\": \"3138\",\r\n \"details\": {\r\n \"operation\": \"c:\\\\AT\\\\sitesroot\\\\0\\\\bin\\\\Plugins\\\\ESRPClient\\\\Win10.x86\\\\signtool.exe verify /pa /tw \\\"c:\\\\Temp\\\\AzureTemp\\\\pqgjedog.0s5\\\\manifest.cat\\\"\",\r\n \"exitCode\": \"1\",\r\n \"stdOut\": \"File: c:\\\\Temp\\\\AzureTemp\\\\pqgjedog.0s5\\\\manifest.cat\\r\\nIndex Algorithm Timestamp \\r\\n========================================\\r\\n\\r\\nNumber of errors: 1\\r\\n\\r\\n\\r\\n\",\r\n \"stdErr\": \"SignTool Error: A certificate chain processed, but terminated in a root\\r\\n\\tcertificate which is not trusted by the trust provider.\\r\\n\\r\\n\"\r\n },\r\n \"innerError\": null\r\n }\r\n }\r\n ],\r\n \"esrpClientSessionGuid\": \"8fbe2d3f-d5a4-4719-8272-b639dba3eace\",\r\n \"version\": \"1.0.0\"\r\n}\r\n2026-05-30T07:12:08.8611515Z:Warning: \r\n\r\n2026-05-30T07:12:08.8611515Z:esrp-client-finalTime: 9391.6821\r\n2026-05-30T07:12:08.8611515Z:Final return code is: 1\r\n2026-05-30T07:12:08.8611515Z:ClientTelemetryDispose started, this is not affecting Exe reliability, only lost telemetry if error happens ***********************\r\n2026-05-30T07:12:08.8674173Z:Client Telemetry: Flushing telemetry buffer with timeout 0 ms\r\n2026-05-30T07:12:08.8701841Z:Client Telemetry: Flushing telemetry buffer completed within timeout (true/false): False\r\n2026-05-30T07:12:08.8701841Z:Client Telemetry: Disposing telemetry buffer and event hub client\r\n2026-05-30T07:12:08.8823601Z:Client Telemetry: Events published in this batch: 4\r\n2026-05-30T07:12:08.8823601Z:Client Telemetry: Events received so far in this session: 11\r\n2026-05-30T07:12:08.8823601Z:Client Telemetry: Events published so far in this session: 11\r\n2026-05-30T07:12:08.9350451Z:event lost number: 0\r\n2026-05-30T07:12:08.9353963Z:total event processed number: 11\r\n2026-05-30T07:12:08.9353963Z:total event received number: 11\r\n2026-05-30T07:12:08.9353963Z:##EsrpClientTelemetry.TotalEventsProcessed##: 11\r\n2026-05-30T07:12:08.9353963Z:##EsrpClientTelemetry.TotalEventsReceived##: 11\r\n2026-05-30T07:12:08.9353963Z:##EsrpClientTelemetry.DisposeTimeMilliseconds##: 72\r\n2026-05-30T07:12:08.9353963Z:ClientTelemetryDispose ended ***********************\r\n","StdErr":"2026-05-30T07:12:08.7454583Z:Error: System.AggregateException: One or more errors occurred. ---> MS.Ess.EsrpClient.Sign.Exceptions.EsrpDigestSignExecuteProcessFailedException: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\pqgjedog.0s5\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\pqgjedog.0s5\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass27_0.<ExecuteDigestSignOperation>b__1()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.<>c__DisplayClass1.<ExecuteAction>b__0()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.ExecuteAction[TResult](Func`1 func)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.ExecuteDigestSignOperation(Input digestSignInput, Operation digestSignOperation, SignOperationData fileEntry, SignStatusResponse completionResponse, SignCommandDefinition param, Guid correlationId, String correlationVector, CancellationToken cancellationToken, String requestId, String tmpDestinationLocation)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.DigestSignInternal(Input digestSignInput, SignCommandDefinition param, Guid& correlationId, String correlationVector, CancellationToken cancellationToken, String groupId, SignOperationData fileEntry, Int32& signedFileCount, Int32& certRefreshCount, ConcurrentBag`1 completionResponses, RetryPolicy`1 operationRetryPolicy, String dynamicCertificateFile, String dynamicCertificateThumbprint, String requestId)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass23_3.<DigestSignAsync>b__6(SignOperationData fileEntry)\r\n --- End of inner exception stack trace ---\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<DigestSignAsync>d__23.MoveNext()\r\n---> (Inner Exception #0) MS.Ess.EsrpClient.Sign.Exceptions.EsrpDigestSignExecuteProcessFailedException: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\pqgjedog.0s5\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\pqgjedog.0s5\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass27_0.<ExecuteDigestSignOperation>b__1()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.<>c__DisplayClass1.<ExecuteAction>b__0()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.ExecuteAction[TResult](Func`1 func)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.ExecuteDigestSignOperation(Input digestSignInput, Operation digestSignOperation, SignOperationData fileEntry, SignStatusResponse completionResponse, SignCommandDefinition param, Guid correlationId, String correlationVector, CancellationToken cancellationToken, String requestId, String tmpDestinationLocation)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.DigestSignInternal(Input digestSignInput, SignCommandDefinition param, Guid& correlationId, String correlationVector, CancellationToken cancellationToken, String groupId, SignOperationData fileEntry, Int32& signedFileCount, Int32& certRefreshCount, ConcurrentBag`1 completionResponses, RetryPolicy`1 operationRetryPolicy, String dynamicCertificateFile, String dynamicCertificateThumbprint, String requestId)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass23_3.<DigestSignAsync>b__6(SignOperationData fileEntry)<---\r\n\r\n","ExitCode":1,"RunningTime":"00:00:10.3256450"} |
| {"StdOut":"******************************************************************************\r\nMachine Information\r\nMachine Name: at-blueYMDLA2\r\nMachine Ip: 10.0.0.5\r\nOperating System: Microsoft Windows NT 10.0.26100.0\r\nUser Name: at-blueYMDLA2$\r\nProcessor Count: 64\r\nProcess Name: EsrpClient\r\nProcess Id: 19100\r\nCaller Program: EsrpClient.exe\r\nIdentity: NT AUTHORITY\\NETWORK SERVICE\r\nProcess Version: 1.2.142+Branch.master.Sha.90404b43284ec55b3e2251d0e272f8932aa583e2\r\nProcess Bitness: 64 bit\r\n******************************************************************************\r\nCommandline received: Sign | -a | c:\\Temp\\AzureTemp\\TFSTemp\\vctmp13468_439268.json | -p | c:\\Temp\\AzureTemp\\TFSTemp\\vctmp13468_872193.json | -c | c:\\Temp\\AzureTemp\\TFSTemp\\vctmp13468_634486.json | -i | c:\\Temp\\AzureTemp\\TFSTemp\\vctmp18224_863351.json | -o | c:\\Temp\\AzureTemp\\TFSTemp\\v47EEE.tmp | -l | Verbose ESRP session Id is: eb2dcb68-642c-4616-a634-d3e42ee7ec3a\r\n2026-05-30T07:12:48.8991077Z:Command you are trying to use is: Sign\r\n2026-05-30T07:12:48.9057946Z:Correlation Vector for this run is: 042c0000-79b6-4627-a85a-3e2f9070d04e\r\n2026-05-30T07:12:48.9057946Z:Groupid for this run is empty\r\n2026-05-30T07:12:49.0223929Z:request signing cert being used is this thumbprint: 42661F29AF78973B30992C76D622575F555E3D84 in store LocalMachine\\My\r\n2026-05-30T07:12:49.0223929Z:Both certificates validation passed.\r\n2026-05-30T07:12:49.0255147Z:The auth cert we choose to use, subject name is: CN=0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f.microsoft.com, thumbprint is: 943507930F1390CF64FC5E9DB45F03091556EEBE\r\n2026-05-30T07:12:49.0877810Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:49Z] ConfidentialClientApplication 37368736 created\r\n2026-05-30T07:12:49.0888031Z:Gateway Client: Enter AuthenticationProvider, no calls to AAD yet, client id is 0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f, and resource url is https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com, tenant id is 33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:49.0943737Z:There is no memory mapped file accociated with this name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:49.0968013Z:AAD auth caching is in use with this name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:49.1066695Z:Validate and Renew Token if necessary finished: 00:00:00.0081966\r\n2026-05-30T07:12:49.2044838Z:Session request is: {\r\n \"expiresAfter\": \"3.00:00:00\",\r\n \"partitionCount\": 0,\r\n \"isProvisionStorage\": true,\r\n \"isLegacyCopsModel\": false,\r\n \"commandName\": \"Sign\",\r\n \"intent\": \"digestsign\",\r\n \"contentType\": \"Bin\",\r\n \"contentOrigin\": \"3rd Party\",\r\n \"productState\": null,\r\n \"audience\": \"External Broad\"\r\n}\r\n2026-05-30T07:12:49.2361536Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:49Z] ConfidentialClientApplication 29578451 created\r\n2026-05-30T07:12:49.2361536Z:Gateway Client: Enter AuthenticationProvider, no calls to AAD yet, client id is 0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f, and resource url is https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com, tenant id is 33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:49.2443484Z:Gateway Client: a new client and client id is created: 23bc0f67-7b81-400f-93d1-24d0f9d9d4e3\r\n2026-05-30T07:12:49.2833087Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:49Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] MSAL MSAL.Desktop with assembly version '4.70.0.0'. CorrelationId(3cfbe3c8-5579-46b7-9902-6216b3a87d52)\r\n2026-05-30T07:12:49.2972191Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:49Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] === AcquireTokenForClientParameters ===\r\nSendX5C: True\r\nForceRefresh: False\r\nAccessTokenHashToRefresh: False\r\n\r\n2026-05-30T07:12:49.3096498Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:49Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] \r\n=== Request Data ===\r\nAuthority Provided? - True\r\nScopes - https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com/.default\r\nExtra Query Params Keys (space separated) - \r\nApiId - AcquireTokenForClient\r\nIsConfidentialClient - True\r\nSendX5C - True\r\nLoginHint ? False\r\nIsBrokerConfigured - False\r\nHomeAccountId - False\r\nCorrelationId - 3cfbe3c8-5579-46b7-9902-6216b3a87d52\r\nUserAssertion set: False\r\nLongRunningOboCacheKey set: False\r\nRegion configured: \r\n\r\n2026-05-30T07:12:49.3108767Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:49Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] === Token Acquisition (ClientCredentialRequest) started:\r\n\t Scopes: https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com/.default\r\n\tAuthority Host: login.microsoftonline.com\r\n2026-05-30T07:12:49.3289105Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:49Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] [Internal cache] Total number of cache partitions found while getting access tokens: 0\r\n2026-05-30T07:12:49.3289105Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:49Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] [FindAccessTokenAsync] Discovered 0 access tokens in cache using partition key: 0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d_AppTokenCache\r\n2026-05-30T07:12:49.3289105Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:49Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] [FindAccessTokenAsync] No access tokens found in the cache. Skipping filtering. \r\n2026-05-30T07:12:49.3392296Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:49Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] [Instance Discovery] Instance discovery is enabled and will be performed\r\n2026-05-30T07:12:49.3434365Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:49Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] [Region discovery] WithAzureRegion not configured. \r\n2026-05-30T07:12:49.3434365Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:49Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] [Region discovery] Not using a regional authority. \r\n2026-05-30T07:12:49.3477594Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:49Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] [Instance Discovery] Tried to use network cache provider for login.microsoftonline.com. Success? False. \r\n2026-05-30T07:12:49.3585247Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:49Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] Fetching instance discovery from the network from host login.microsoftonline.com. \r\n2026-05-30T07:12:49.3776939Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:49Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] Starting [Oauth2Client] Sending GET request \r\n2026-05-30T07:12:49.3818954Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:49Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] Starting [HttpManager] ExecuteAsync\r\n2026-05-30T07:12:49.3879176Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:49Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] [HttpManager] Sending request. Method: GET. Host: https://login.microsoftonline.com. Binding Certificate: False \r\n2026-05-30T07:12:49.8966312Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:49Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] [HttpManager] Received response. Status code: OK. \r\n2026-05-30T07:12:49.9094797Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:49Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] Finished [HttpManager] ExecuteAsync in 527 ms\r\n2026-05-30T07:12:49.9094797Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:49Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] Finished [Oauth2Client] Sending GET request in 532 ms\r\n2026-05-30T07:12:49.9201366Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:49Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] Starting [OAuth2Client] Deserializing response\r\n2026-05-30T07:12:50.0469915Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] Finished [OAuth2Client] Deserializing response in 126 ms\r\n2026-05-30T07:12:50.0478181Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] [Instance Discovery] Tried to use network cache provider for login.microsoftonline.com. Success? True. \r\n2026-05-30T07:12:50.0481986Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] [Instance Discovery] After hitting the discovery endpoint, the network provider found an entry for login.microsoftonline.com ? True. \r\n2026-05-30T07:12:50.0527000Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] Authority validation enabled? True. \r\n2026-05-30T07:12:50.0527000Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] Authority validation - is known env? True. \r\n2026-05-30T07:12:50.0853245Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] Starting TokenClient:SendTokenRequestAsync\r\n2026-05-30T07:12:50.0922990Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] [TokenClient] Before adding the client assertion / secret\r\n2026-05-30T07:12:50.0943447Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] Building assertion from certificate with clientId: 0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f at endpoint: https://login.microsoftonline.com/33e01921-4d64-4f8c-a055-5bdaffd5e33d/oauth2/v2.0/token\r\n2026-05-30T07:12:50.0943447Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] Proceeding with JWT token creation and adding client assertion.\r\n2026-05-30T07:12:50.1325398Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] [TokenClient] After adding the client assertion / secret\r\n2026-05-30T07:12:50.1437715Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] [Token Client] Fetching MsalTokenResponse .... \r\n2026-05-30T07:12:50.1437715Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] Starting [Oauth2Client] Sending POST request \r\n2026-05-30T07:12:50.1504233Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] Starting [HttpManager] ExecuteAsync\r\n2026-05-30T07:12:50.1504233Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] [HttpManager] Sending request. Method: POST. Host: https://login.microsoftonline.com. Binding Certificate: False \r\n2026-05-30T07:12:50.3256669Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] [HttpManager] Received response. Status code: OK. \r\n2026-05-30T07:12:50.3256669Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] Finished [HttpManager] ExecuteAsync in 176 ms\r\n2026-05-30T07:12:50.3256669Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] Finished [Oauth2Client] Sending POST request in 183 ms\r\n2026-05-30T07:12:50.3274596Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] Starting [OAuth2Client] Deserializing response\r\n2026-05-30T07:12:50.3409169Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] Finished [OAuth2Client] Deserializing response in 14 ms\r\n2026-05-30T07:12:50.3409169Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] ScopeSet was missing from the token response, so using developer provided scopes in the result. \r\n2026-05-30T07:12:50.3409169Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] Finished TokenClient:SendTokenRequestAsync in 256 ms\r\n2026-05-30T07:12:50.3427941Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] Checking client info returned from the server..\r\n2026-05-30T07:12:50.3427941Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] Saving token response to cache..\r\n2026-05-30T07:12:50.3533486Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] \r\n[MsalTokenResponse]\r\nError: \r\nErrorDescription: \r\nScopes: https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com/.default\r\nExpiresIn: 86399\r\nRefreshIn: 43199\r\nAccessToken returned: True\r\nAccessToken Type: Bearer\r\nRefreshToken returned: False\r\nIdToken returned: False\r\nClientInfo returned: False\r\nFamilyId: \r\nWamAccountId exists: False\r\n2026-05-30T07:12:50.3538120Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] [SaveTokenResponseAsync] ID Token not present in response. \r\n2026-05-30T07:12:50.3553529Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] Cannot determine home account ID - or id token or no client info and no subject \r\n2026-05-30T07:12:50.3613727Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] [SaveTokenResponseAsync] Entering token cache semaphore. Count Real semaphore: True. Count: 1.\r\n2026-05-30T07:12:50.3613727Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] [SaveTokenResponseAsync] Entered token cache semaphore. \r\n2026-05-30T07:12:50.3613727Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] [SaveTokenResponseAsync] Saving AT in cache and removing overlapping ATs...\r\n2026-05-30T07:12:50.3633779Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] Looking for scopes for the authority in the cache which intersect with https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com/.default\r\n2026-05-30T07:12:50.3633779Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z] [Internal cache] Total number of cache partitions found while getting access tokens: 0\r\n2026-05-30T07:12:50.3633779Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] Intersecting scope entries count - 0\r\n2026-05-30T07:12:50.3660100Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] Not saving to ADAL legacy cache. \r\n2026-05-30T07:12:50.3660100Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] [SaveTokenResponseAsync] Released token cache semaphore. \r\n2026-05-30T07:12:50.3693982Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] \r\n\t=== Token Acquisition finished successfully:\r\n2026-05-30T07:12:50.3708901Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] AT expiration time: 5/31/2026 7:12:49 AM +00:00, scopes: https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com/.default. source: IdentityProvider\r\n2026-05-30T07:12:50.3708901Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] Fetched access token from host login.microsoftonline.com. \r\n2026-05-30T07:12:50.3729683Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] \r\n[LogMetricsFromAuthResult] Cache Refresh Reason: NoCachedAccessToken\r\n[LogMetricsFromAuthResult] DurationInCacheInMs: 0\r\n[LogMetricsFromAuthResult] DurationTotalInMs: 1068\r\n[LogMetricsFromAuthResult] DurationInHttpInMs: 683\r\n2026-05-30T07:12:50.3729683Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:50Z - 3cfbe3c8-5579-46b7-9902-6216b3a87d52] TokenEndpoint: ****\r\n2026-05-30T07:12:50.3883420Z:Use AAD token. CERT\r\n2026-05-30T07:12:50.4009545Z:New Token Acquisition Time: 00:00:01.1466522\r\n2026-05-30T07:12:50.4292290Z:Gateway Client: the client id that is sending a request is: 23bc0f67-7b81-400f-93d1-24d0f9d9d4e3\r\n2026-05-30T07:12:51.2756730Z:Gateway Client: response send time from cloud gateway: 2026-05-30T07:12:51.1128676+00:00\r\n2026-05-30T07:12:51.2756730Z:Gateway Client: response receive time from client SendAsync: 2026-05-30T07:12:51.2756730+00:00\r\n2026-05-30T07:12:51.2756730Z:Gateway Client: response duration time between cloud gateway and client SendAsync: 00:00:00.1628054\r\n2026-05-30T07:12:51.3974801Z:Session request requestid: 283cc409-188a-4c46-87a5-85d097f79706, and submission status is: Pass\r\n2026-05-30T07:12:51.4571390Z:Provision storage complete. Total shards: 100\r\n2026-05-30T07:12:51.4606683Z:Loading DigestSignErrorMappingCache mapping info\r\n2026-05-30T07:12:51.4704164Z:DigestSignErrorMappingCache from server, # of DigestSignOperationErrorPatterns object we get is :: \r\n2026-05-30T07:12:51.4995063Z:Consolidate DigestSignErrorMappingCache, # of DigestSignOperationErrorPatterns object we get is :: 34\r\n2026-05-30T07:12:51.5006094Z:Successfully retrieved policy: policy is {\"policy\":{\"id\":\"0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f-0\",\"workflowExecutionType\":3}}\r\n2026-05-30T07:12:51.5109841Z:Successfully get telemetry connection string: Endpo......\r\n2026-05-30T07:12:51.5135344Z:Warning: \r\n2026-05-30T07:12:51.5169062Z:IAuthInfo constructed: {\r\n \"authenticationType\": \"AAD_CERT\",\r\n \"clientId\": \"0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f\",\r\n \"tenantId\": \"33e01921-4d64-4f8c-a055-5bdaffd5e33d\",\r\n \"aadAuthorityBaseUri\": \"https://login.microsoftonline.com/\",\r\n \"authCert\": {\r\n \"storeLocation\": \"LocalMachine\",\r\n \"storeName\": \"My\",\r\n \"subjectName\": \"CN=0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f.microsoft.com\",\r\n \"sendX5c\": true,\r\n \"withAzureRegion\": false,\r\n \"getCertFromKeyVault\": false,\r\n \"keyVaultName\": null,\r\n \"keyVaultCertName\": null\r\n },\r\n \"requestSigningCert\": {\r\n \"storeLocation\": \"LocalMachine\",\r\n \"storeName\": \"My\",\r\n \"subjectName\": \"CN=0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f\",\r\n \"sendX5c\": false,\r\n \"withAzureRegion\": false,\r\n \"getCertFromKeyVault\": false,\r\n \"keyVaultName\": null,\r\n \"keyVaultCertName\": null\r\n },\r\n \"oAuthToken\": null,\r\n \"version\": \"1.0.0\",\r\n \"esrpClientId\": \"0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f\",\r\n \"federatedTokenData\": null,\r\n \"federatedTokenPath\": null\r\n}\r\n2026-05-30T07:12:51.5182298Z:IPolicyInfo constructed: {\r\n \"intent\": \"digestsign\",\r\n \"contentType\": \"Bin\",\r\n \"contentOrigin\": \"3rd Party\",\r\n \"productState\": null,\r\n \"audience\": \"External Broad\",\r\n \"version\": \"1.0.0\"\r\n}\r\n2026-05-30T07:12:51.5226351Z:IConfigInfo constructed: {\r\n \"esrpApiBaseUri\": \"https://api.esrp.microsoft.com/api/v2/\",\r\n \"esrpSessionTimeoutInSec\": 60,\r\n \"minThreadPoolThreads\": 256,\r\n \"maxDegreeOfParallelism\": 256,\r\n \"exponentialFirstFastRetry\": true,\r\n \"exponentialRetryCount\": 2,\r\n \"exponentialRetryDeltaBackOff\": \"00:00:05\",\r\n \"exponentialRetryMaxBackOff\": \"00:01:00\",\r\n \"exponentialRetryMinBackOff\": \"00:00:03\",\r\n \"appDataFolder\": \"C:\\\\Windows\\\\ServiceProfiles\\\\NetworkService\\\\AppData\\\\Local\",\r\n \"certificateCacheFolder\": null,\r\n \"version\": \"1.0.0\",\r\n \"exitOnFlaggedFile\": false,\r\n \"flaggedFileClientWaitTimeout\": \"1.00:00:00\",\r\n \"servicePointManagerDefaultConnectionLimit\": 256,\r\n \"isOnPremGateway\": false,\r\n \"diagnosticListeners\": null,\r\n \"securityProtocolType\": \"Tls12\",\r\n \"parallelOperationsInFileUploadDownload\": 300,\r\n \"maxTelemetryBuffer\": 200000,\r\n \"telemetryTimeoutInSec\": 0,\r\n \"resourceUri\": \"https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com\",\r\n \"cacheRootFolder\": null,\r\n \"cachedFileTTLInMin\": 7200\r\n}\r\n2026-05-30T07:12:51.7765109Z:Client Telemetry: Xpert agent is not running on the machine. Using events hub for processing client telemetry.\r\n2026-05-30T07:12:51.9820297Z:Key: TotalSignOperationDataCount, Value: 1\r\n2026-05-30T07:12:51.9996553Z:Start Time:5/30/2026 7:12:51 AM, Starting the sign workflow...\r\n2026-05-30T07:12:52.0116759Z:some input info: {\r\n \"contextData\": {\r\n \"build_buildnumber\": \"AzureDevOps_M273_20260511.16\",\r\n \"esrpClientVersion\": \"1.2.142+Branch.master.Sha.90404b43284ec55b3e2251d0e272f8932aa583e2\",\r\n \"userIpAddress\": \"10.0.0.5\",\r\n \"userAgent\": \"at-blueYMDLA2\"\r\n },\r\n \"sourceDirectory\": null,\r\n \"sourceLocation\": \"c:\\\\Temp\\\\AzureTemp\\\\hmoiwrgn.03s\\\\manifest.cat\",\r\n \"destinationDirectory\": null,\r\n \"destinationLocation\": \"c:\\\\Temp\\\\AzureTemp\\\\hmoiwrgn.03s\\\\manifest.cat\",\r\n \"sizeInBytes\": 0,\r\n \"name\": \"manifest.cat\",\r\n \"isSuccess\": null,\r\n \"operationStartedAt\": \"0001-01-01T00:00:00+00:00\",\r\n \"operationEndedAt\": \"0001-01-01T00:00:00+00:00\",\r\n \"operationDurationMS\": 0,\r\n \"processName\": \"EsrpClient\",\r\n \"processId\": 19100,\r\n \"processVersion\": \"1.2.142+Branch.master.Sha.90404b43284ec55b3e2251d0e272f8932aa583e2\",\r\n \"customerCorrelationId\": null,\r\n \"esrpClientSessionGuid\": \"eb2dcb68-642c-4616-a634-d3e42ee7ec3a\",\r\n \"callerProgram\": \"EsrpClient\",\r\n \"indentity\": \"NT AUTHORITY\\\\NETWORK SERVICE\",\r\n \"clientId\": \"0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f\",\r\n \"exceptionData\": null,\r\n \"apiBaseUrl\": \"https://api.esrp.microsoft.com/api/v2/\",\r\n \"handlerWorkflow\": \"Sign\",\r\n \"submissionRequest\": {\r\n \"contextData\": {\r\n \"build_buildnumber\": \"AzureDevOps_M273_20260511.16\",\r\n \"esrpClientVersion\": \"1.2.142+Branch.master.Sha.90404b43284ec55b3e2251d0e272f8932aa583e2\",\r\n \"userIpAddress\": \"10.0.0.5\",\r\n \"userAgent\": \"at-blueYMDLA2\"\r\n },\r\n \"groupId\": null,\r\n \"correlationVector\": \"042c0000-79b6-4627-a85a-3e2f9070d04e\",\r\n \"driEmail\": null,\r\n \"version\": \"1.0.0\"\r\n },\r\n \"policyInfo\": {\r\n \"intent\": \"digestsign\",\r\n \"contentType\": \"Bin\",\r\n \"contentOrigin\": \"3rd Party\",\r\n \"productState\": null,\r\n \"audience\": \"External Broad\",\r\n \"version\": \"1.0.0\"\r\n },\r\n \"osVersion\": \"Microsoft Windows NT 10.0.26100.0\",\r\n \"executingMachineIPAddress\": \"10.0.0.5\"\r\n}\r\n2026-05-30T07:12:52.0587816Z:Executing SignWorkflowType is DigestSignStaticAzure\r\n2026-05-30T07:12:52.0746320Z:Source file \"c:\\Temp\\AzureTemp\\hmoiwrgn.03s\\manifest.cat\" hashed in 3 ms\r\n2026-05-30T07:12:52.1208120Z:Thread: 1 - Certificate file mutex \"dgCw67UWqL5qguxXlqJT6HYyfoz7ew0nugCPhNtogvc=_CP-464321\" acquired (waited 0 ms).\r\nMSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z] ConfidentialClientApplication 44123454 created\r\n2026-05-30T07:12:52.1340542Z:Gateway Client: Enter AuthenticationProvider, no calls to AAD yet, client id is 0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f, and resource url is https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com, tenant id is 33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:52.1343738Z:Cached token found with name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:52.1352601Z:AAD auth caching is in use with this name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:52.2263804Z:Global cached token is valid from 5/30/2026 7:07:50 AM to 5/31/2026 7:12:50 AM (UTC). Total validity from current time is 86397.7736196 seconds\r\n2026-05-30T07:12:52.2263804Z:Validate and Renew Token if necessary finished: 00:00:00.0922064\r\nMSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z] ConfidentialClientApplication 20852350 created\r\n2026-05-30T07:12:52.2277078Z:Gateway Client: Enter AuthenticationProvider, no calls to AAD yet, client id is 0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f, and resource url is https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com, tenant id is 33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:52.2277078Z:Gateway Client: a new client and client id is created: a5fb8562-7be7-4e0e-93c3-53caa4577bae\r\n2026-05-30T07:12:52.2277078Z:5/30/2026 7:12:52 AM +00:00:Digest Signing Factory Client type loaded is - [AzureGatewaySubmitter].\r\n2026-05-30T07:12:52.2390212Z:Existing Token Acquisition Time: 00:00:00.0004160\r\n2026-05-30T07:12:52.3155715Z:Gateway Client: the client id that is sending a request is: a5fb8562-7be7-4e0e-93c3-53caa4577bae\r\n2026-05-30T07:12:52.5947182Z:Gateway Client: response send time from cloud gateway: 2026-05-30T07:12:52.5624651+00:00\r\n2026-05-30T07:12:52.5947182Z:Gateway Client: response receive time from client SendAsync: 2026-05-30T07:12:52.5947182+00:00\r\n2026-05-30T07:12:52.5947182Z:Gateway Client: response duration time between cloud gateway and client SendAsync: 00:00:00.0322531\r\n2026-05-30T07:12:52.5964262Z:Gateway Client: response time after converting from http response to object: 2026-05-30T07:12:52.5964262+00:00\r\n2026-05-30T07:12:52.8426495Z:Client Telemetry: Events published in this batch: 4\r\n2026-05-30T07:12:52.8426495Z:Client Telemetry: Events received so far in this session: 4\r\n2026-05-30T07:12:52.8426495Z:Client Telemetry: Events published so far in this session: 4\r\n2026-05-30T07:12:53.1101460Z:Existing Token Acquisition Time: 00:00:00.0006969\r\n2026-05-30T07:12:53.1138623Z:Gateway Client: the client id that is sending a request is: a5fb8562-7be7-4e0e-93c3-53caa4577bae\r\n2026-05-30T07:12:53.2717368Z:Gateway Client: response send time from cloud gateway: 2026-05-30T07:12:53.2409019+00:00\r\n2026-05-30T07:12:53.2717368Z:Gateway Client: response receive time from client SendAsync: 2026-05-30T07:12:53.2717368+00:00\r\n2026-05-30T07:12:53.2717368Z:Gateway Client: response duration time between cloud gateway and client SendAsync: 00:00:00.0308349\r\n2026-05-30T07:12:53.2809115Z:Gateway Client: response time after converting from http response to object: 2026-05-30T07:12:53.2809115+00:00\r\n2026-05-30T07:12:53.2863160Z:Reading thumbprint from \"C:\\Windows\\ServiceProfiles\\NetworkService\\AppData\\Local\\EsrpClient_eb2dcb68642c4616a634d3e42ee7ec3a\\Certificates\\CP-464321.p7b\" (9692 bytes)...\r\n2026-05-30T07:12:53.3166765Z:Thread: 1 - Wrote new certificate file to \"C:\\Windows\\ServiceProfiles\\NetworkService\\AppData\\Local\\EsrpClient_eb2dcb68642c4616a634d3e42ee7ec3a\\Certificates\\CP-464321.p7b\".\r\n2026-05-30T07:12:53.3166765Z:Thread: 1 - Certificate file mutex \"dgCw67UWqL5qguxXlqJT6HYyfoz7ew0nugCPhNtogvc=_CP-464321\" released (held for 1196 ms).\r\n2026-05-30T07:12:53.3179894Z:digest sign request expire time is: 5/30/2026 7:13:51 AM\r\n2026-05-30T07:12:56.8690320Z:Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe sign /NPH /fd \"SHA256\" /f \"C:\\Windows\\ServiceProfiles\\NetworkService\\AppData\\Local\\EsrpClient_eb2dcb68642c4616a634d3e42ee7ec3a\\Certificates\\CP-464321.p7b\" /sha1 \"464FA2A9D4A43EA5C3E53CE4562B501CB6EFA47F\" /du \"https://www.1eswiki.com/wiki/ADO_Manifest_Generator\" /d \"Packaging SSSC Codesign - DigestSign\" /tr \"http://aztss.trafficmanager.net/TSS/HttpTspServer\" /td sha256 /dlib \"c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\EsrpClient.Sign.DigestSignLib.dll\" /dmdf \"C:\\Windows\\ServiceProfiles\\NetworkService\\AppData\\Local\\EsrpClient_eb2dcb68642c4616a634d3e42ee7ec3a\\321FAC76AC764227BE02A893544FF1C9.json\" \"c:\\Temp\\AzureTemp\\hmoiwrgn.03s\\manifest.cat\" completed in 3550 ms\r\nExit Code: 0\r\nStdOut:\r\nDone Adding Additional Store\r\n\r\nESRP Digest Signing\r\n\r\n2026-05-30T07:12:54.3392551Z:Digest Signer : SecurityProtocolType used from the metadata info is : Tls12\r\nMSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:54Z] ConfidentialClientApplication 654897 created\r\n2026-05-30T07:12:54.4857677Z:Cached token found with name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:54.4877760Z:AAD auth caching is in use with this name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:54.6055909Z:Global cached token is valid from 5/30/2026 7:07:50 AM to 5/31/2026 7:12:50 AM (UTC). Total validity from current time is 86395.3947338 seconds\r\n2026-05-30T07:12:54.6055909Z:Validate and Renew Token if necessary finished: 00:00:00.1164265\r\nMSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:54Z] ConfidentialClientApplication 49044892 created\r\n2026-05-30T07:12:54.6784519Z:Existing Token Acquisition Time: 00:00:00.0029579\r\n2026-05-30T07:12:55.6213645Z:Gateway Submission Request Signing Time: 00:00:00.1104941\r\n2026-05-30T07:12:55.6213645Z:Gateway Submission Request Send Time: 00:00:00.8111692\r\n2026-05-30T07:12:55.6213645Z:Gateway Submission Overall Submission Time: 00:00:00.9395436\r\n2026-05-30T07:12:55.6213645Z:Operation ID: 131241f8-8ea9-4cb6-897e-b7da2dab0366\r\n2026-05-30T07:12:56.1304965Z:Gateway Status Delay Time: 00:00:00.5089002\r\n2026-05-30T07:12:56.1407320Z:Existing Token Acquisition Time: 00:00:00.0008133\r\n2026-05-30T07:12:56.2602496Z:Gateway Status Request Send Time: 00:00:00.1140273\r\n2026-05-30T07:12:56.2602496Z:Gateway Status Overall Time: 00:00:00.1164672\r\n2026-05-30T07:12:56.2680811Z:Gateway Submission Duration: 1280\r\n2026-05-30T07:12:56.2680811Z:Gateway Submission Attempts: 1\r\n2026-05-30T07:12:56.2680811Z:Gateway GetStatus Duration: 129\r\n2026-05-30T07:12:56.2680811Z:Gateway GetStatus Attempts: 1\r\n2026-05-30T07:12:56.2680811Z:$$5a9f5111cf8f42a0a0edc419862b95dc##_ThrottleCount: 0\r\n2026-05-30T07:12:56.2680811Z:$$65265a48891640eb86e148d3c9a627fa##_ThrottledTimeInSec: 0\r\n2026-05-30T07:12:56.2680811Z:Service Call: 2067.2867 ms\r\nSuccessfully signed: c:\\Temp\\AzureTemp\\hmoiwrgn.03s\\manifest.cat\r\n\r\n\r\n\r\nStdErr:\r\n\r\n\r\n\r\n\r\n2026-05-30T07:12:56.9045688Z:Client Telemetry: Events published in this batch: 1\r\n2026-05-30T07:12:56.9045688Z:Client Telemetry: Events received so far in this session: 5\r\n2026-05-30T07:12:56.9045688Z:Client Telemetry: Events published so far in this session: 5\r\n2026-05-30T07:12:57.0641486Z:Warning: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\hmoiwrgn.03s\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\hmoiwrgn.03s\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n2026-05-30T07:12:57.0661574Z:Warning: Warning: Operation Error (1) - Operation: signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\hmoiwrgn.03s\\manifest.cat\" - Retrying in 500 ms...\r\n2026-05-30T07:12:57.2240363Z:Warning: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\hmoiwrgn.03s\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\hmoiwrgn.03s\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n2026-05-30T07:12:57.2240363Z:Warning: Warning: Operation Error (2) - Operation: signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\hmoiwrgn.03s\\manifest.cat\" - Retrying in 500 ms...\r\n2026-05-30T07:12:57.4128603Z:Client Telemetry: Events published in this batch: 2\r\n2026-05-30T07:12:57.4128603Z:Client Telemetry: Events received so far in this session: 7\r\n2026-05-30T07:12:57.4128603Z:Client Telemetry: Events published so far in this session: 7\r\n2026-05-30T07:12:57.8427726Z:Warning: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\hmoiwrgn.03s\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\hmoiwrgn.03s\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n2026-05-30T07:12:57.8962313Z:\r\n2026-05-30T07:12:57.8966657Z:Error: System.AggregateException: One or more errors occurred. ---> MS.Ess.EsrpClient.Sign.Exceptions.EsrpDigestSignExecuteProcessFailedException: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\hmoiwrgn.03s\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\hmoiwrgn.03s\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass27_0.<ExecuteDigestSignOperation>b__1()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.<>c__DisplayClass1.<ExecuteAction>b__0()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.ExecuteAction[TResult](Func`1 func)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.ExecuteDigestSignOperation(Input digestSignInput, Operation digestSignOperation, SignOperationData fileEntry, SignStatusResponse completionResponse, SignCommandDefinition param, Guid correlationId, String correlationVector, CancellationToken cancellationToken, String requestId, String tmpDestinationLocation)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.DigestSignInternal(Input digestSignInput, SignCommandDefinition param, Guid& correlationId, String correlationVector, CancellationToken cancellationToken, String groupId, SignOperationData fileEntry, Int32& signedFileCount, Int32& certRefreshCount, ConcurrentBag`1 completionResponses, RetryPolicy`1 operationRetryPolicy, String dynamicCertificateFile, String dynamicCertificateThumbprint, String requestId)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass23_3.<DigestSignAsync>b__6(SignOperationData fileEntry)\r\n --- End of inner exception stack trace ---\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<DigestSignAsync>d__23.MoveNext()\r\n---> (Inner Exception #0) MS.Ess.EsrpClient.Sign.Exceptions.EsrpDigestSignExecuteProcessFailedException: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\hmoiwrgn.03s\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\hmoiwrgn.03s\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass27_0.<ExecuteDigestSignOperation>b__1()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.<>c__DisplayClass1.<ExecuteAction>b__0()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.ExecuteAction[TResult](Func`1 func)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.ExecuteDigestSignOperation(Input digestSignInput, Operation digestSignOperation, SignOperationData fileEntry, SignStatusResponse completionResponse, SignCommandDefinition param, Guid correlationId, String correlationVector, CancellationToken cancellationToken, String requestId, String tmpDestinationLocation)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.DigestSignInternal(Input digestSignInput, SignCommandDefinition param, Guid& correlationId, String correlationVector, CancellationToken cancellationToken, String groupId, SignOperationData fileEntry, Int32& signedFileCount, Int32& certRefreshCount, ConcurrentBag`1 completionResponses, RetryPolicy`1 operationRetryPolicy, String dynamicCertificateFile, String dynamicCertificateThumbprint, String requestId)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass23_3.<DigestSignAsync>b__6(SignOperationData fileEntry)<---\r\n\r\n2026-05-30T07:12:57.9372892Z:Constructing EsrpClientResponse\r\n2026-05-30T07:12:57.9384302Z:EsrpClientResponse is constructed\r\n2026-05-30T07:12:57.9439448Z:Client Telemetry: Events published in this batch: 2\r\n2026-05-30T07:12:57.9439448Z:Client Telemetry: Events received so far in this session: 9\r\n2026-05-30T07:12:57.9439448Z:Client Telemetry: Events published so far in this session: 9\r\n2026-05-30T07:12:57.9720961Z:OperationDurationMs: 5888\r\n2026-05-30T07:12:57.9723990Z:DynamicCertGenerationTimeMs: 0\r\n2026-05-30T07:12:57.9723990Z:TimeToGetStorageShradsMs: 0\r\n2026-05-30T07:12:57.9723990Z:TotalScanSubmissionTimeMs: 0\r\n2026-05-30T07:12:57.9723990Z:ThrottleCount: 0\r\n2026-05-30T07:12:57.9723990Z:ThrottledTimeInSec: 0\r\n2026-05-30T07:12:57.9723990Z:\r\n2026-05-30T07:12:57.9723990Z:0/1 files signed in 00:00:05.888 (0 files/s, total number of certs refreshed: 0)\r\n2026-05-30T07:12:57.9786256Z:result json is: {\r\n \"submissionResponses\": [\r\n {\r\n \"fileStatusDetail\": [\r\n {\r\n \"sourceHash\": \"ZPcOzdZSOah446a89dX6q5zej3urH82sLposgPl6dbw=\",\r\n \"hashType\": \"sha256\",\r\n \"destinationHash\": null,\r\n \"certificateThumbprint\": null,\r\n \"destinationLocation\": \"c:\\\\Temp\\\\AzureTemp\\\\hmoiwrgn.03s\\\\manifest.cat\",\r\n \"destinationFileSizeInBytes\": 0,\r\n \"sourceLocation\": \"c:\\\\Temp\\\\AzureTemp\\\\hmoiwrgn.03s\\\\manifest.cat\"\r\n }\r\n ],\r\n \"operationId\": \"131241f8-8ea9-4cb6-897e-b7da2dab0366\",\r\n \"customerCorrelationId\": \"042c0000-79b6-4627-a85a-3e2f9070d04e\",\r\n \"statusCode\": \"failCanRetry\",\r\n \"errorInfo\": {\r\n \"code\": \"3138\",\r\n \"details\": {\r\n \"operation\": \"c:\\\\AT\\\\sitesroot\\\\0\\\\bin\\\\Plugins\\\\ESRPClient\\\\Win10.x86\\\\signtool.exe verify /pa /tw \\\"c:\\\\Temp\\\\AzureTemp\\\\hmoiwrgn.03s\\\\manifest.cat\\\"\",\r\n \"exitCode\": \"1\",\r\n \"stdOut\": \"File: c:\\\\Temp\\\\AzureTemp\\\\hmoiwrgn.03s\\\\manifest.cat\\r\\nIndex Algorithm Timestamp \\r\\n========================================\\r\\n\\r\\nNumber of errors: 1\\r\\n\\r\\n\\r\\n\",\r\n \"stdErr\": \"SignTool Error: A certificate chain processed, but terminated in a root\\r\\n\\tcertificate which is not trusted by the trust provider.\\r\\n\\r\\n\"\r\n },\r\n \"innerError\": null\r\n }\r\n }\r\n ],\r\n \"esrpClientSessionGuid\": \"eb2dcb68-642c-4616-a634-d3e42ee7ec3a\",\r\n \"version\": \"1.0.0\"\r\n}\r\n2026-05-30T07:12:58.0316520Z:Warning: \r\n\r\n2026-05-30T07:12:58.0316520Z:esrp-client-finalTime: 10107.7925\r\n2026-05-30T07:12:58.0316520Z:Final return code is: 1\r\n2026-05-30T07:12:58.0316520Z:ClientTelemetryDispose started, this is not affecting Exe reliability, only lost telemetry if error happens ***********************\r\n2026-05-30T07:12:58.0360508Z:Client Telemetry: Flushing telemetry buffer with timeout 0 ms\r\n2026-05-30T07:12:58.0387368Z:Client Telemetry: Flushing telemetry buffer completed within timeout (true/false): False\r\n2026-05-30T07:12:58.0387368Z:Client Telemetry: Disposing telemetry buffer and event hub client\r\n2026-05-30T07:12:58.0427385Z:Client Telemetry: Events published in this batch: 2\r\n2026-05-30T07:12:58.0427385Z:Client Telemetry: Events received so far in this session: 11\r\n2026-05-30T07:12:58.0427385Z:Client Telemetry: Events published so far in this session: 11\r\n2026-05-30T07:12:58.1022091Z:event lost number: 0\r\n2026-05-30T07:12:58.1022091Z:total event processed number: 11\r\n2026-05-30T07:12:58.1022091Z:total event received number: 11\r\n2026-05-30T07:12:58.1022091Z:##EsrpClientTelemetry.TotalEventsProcessed##: 11\r\n2026-05-30T07:12:58.1022091Z:##EsrpClientTelemetry.TotalEventsReceived##: 11\r\n2026-05-30T07:12:58.1022091Z:##EsrpClientTelemetry.DisposeTimeMilliseconds##: 71\r\n2026-05-30T07:12:58.1022091Z:ClientTelemetryDispose ended ***********************\r\n","StdErr":"2026-05-30T07:12:57.8966657Z:Error: System.AggregateException: One or more errors occurred. ---> MS.Ess.EsrpClient.Sign.Exceptions.EsrpDigestSignExecuteProcessFailedException: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\hmoiwrgn.03s\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\hmoiwrgn.03s\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass27_0.<ExecuteDigestSignOperation>b__1()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.<>c__DisplayClass1.<ExecuteAction>b__0()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.ExecuteAction[TResult](Func`1 func)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.ExecuteDigestSignOperation(Input digestSignInput, Operation digestSignOperation, SignOperationData fileEntry, SignStatusResponse completionResponse, SignCommandDefinition param, Guid correlationId, String correlationVector, CancellationToken cancellationToken, String requestId, String tmpDestinationLocation)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.DigestSignInternal(Input digestSignInput, SignCommandDefinition param, Guid& correlationId, String correlationVector, CancellationToken cancellationToken, String groupId, SignOperationData fileEntry, Int32& signedFileCount, Int32& certRefreshCount, ConcurrentBag`1 completionResponses, RetryPolicy`1 operationRetryPolicy, String dynamicCertificateFile, String dynamicCertificateThumbprint, String requestId)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass23_3.<DigestSignAsync>b__6(SignOperationData fileEntry)\r\n --- End of inner exception stack trace ---\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<DigestSignAsync>d__23.MoveNext()\r\n---> (Inner Exception #0) MS.Ess.EsrpClient.Sign.Exceptions.EsrpDigestSignExecuteProcessFailedException: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\hmoiwrgn.03s\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\hmoiwrgn.03s\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass27_0.<ExecuteDigestSignOperation>b__1()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.<>c__DisplayClass1.<ExecuteAction>b__0()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.ExecuteAction[TResult](Func`1 func)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.ExecuteDigestSignOperation(Input digestSignInput, Operation digestSignOperation, SignOperationData fileEntry, SignStatusResponse completionResponse, SignCommandDefinition param, Guid correlationId, String correlationVector, CancellationToken cancellationToken, String requestId, String tmpDestinationLocation)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.DigestSignInternal(Input digestSignInput, SignCommandDefinition param, Guid& correlationId, String correlationVector, CancellationToken cancellationToken, String groupId, SignOperationData fileEntry, Int32& signedFileCount, Int32& certRefreshCount, ConcurrentBag`1 completionResponses, RetryPolicy`1 operationRetryPolicy, String dynamicCertificateFile, String dynamicCertificateThumbprint, String requestId)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass23_3.<DigestSignAsync>b__6(SignOperationData fileEntry)<---\r\n\r\n","ExitCode":1,"RunningTime":"00:00:10.9891974"} |
| {"StdOut":"******************************************************************************\r\nMachine Information\r\nMachine Name: at-blueOHG31D\r\nMachine Ip: 10.0.0.26\r\nOperating System: Microsoft Windows NT 10.0.26100.0\r\nUser Name: at-blueOHG31D$\r\nProcessor Count: 64\r\nProcess Name: EsrpClient\r\nProcess Id: 13532\r\nCaller Program: EsrpClient.exe\r\nIdentity: NT AUTHORITY\\NETWORK SERVICE\r\nProcess Version: 1.2.142+Branch.master.Sha.90404b43284ec55b3e2251d0e272f8932aa583e2\r\nProcess Bitness: 64 bit\r\n******************************************************************************\r\nCommandline received: Sign | -a | c:\\Temp\\AzureTemp\\TFSTemp\\vctmp14540_418075.json | -p | c:\\Temp\\AzureTemp\\TFSTemp\\vctmp14540_154641.json | -c | c:\\Temp\\AzureTemp\\TFSTemp\\vctmp14540_163561.json | -i | c:\\Temp\\AzureTemp\\TFSTemp\\vctmp7656_969702.json | -o | c:\\Temp\\AzureTemp\\TFSTemp\\v3682E9.tmp | -l | Verbose ESRP session Id is: bcd3eec4-b645-4e1b-aedb-4c9525ee35ef\r\n2026-05-30T07:18:38.7623043Z:Command you are trying to use is: Sign\r\n2026-05-30T07:18:38.7691690Z:Correlation Vector for this run is: ed85143f-fbdc-4a45-87b1-b09e31f1e18c\r\n2026-05-30T07:18:38.7691690Z:Groupid for this run is empty\r\n2026-05-30T07:18:38.9034272Z:request signing cert being used is this thumbprint: 42661F29AF78973B30992C76D622575F555E3D84 in store LocalMachine\\My\r\n2026-05-30T07:18:38.9034272Z:Both certificates validation passed.\r\n2026-05-30T07:18:38.9055243Z:The auth cert we choose to use, subject name is: CN=0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f.microsoft.com, thumbprint is: 943507930F1390CF64FC5E9DB45F03091556EEBE\r\n2026-05-30T07:18:38.9721948Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:18:38Z] ConfidentialClientApplication 37368736 created\r\n2026-05-30T07:18:38.9729594Z:Gateway Client: Enter AuthenticationProvider, no calls to AAD yet, client id is 0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f, and resource url is https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com, tenant id is 33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:18:38.9926271Z:Cached token found with name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:18:38.9950545Z:AAD auth caching is in use with this name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:18:39.1390472Z:Global cached token is valid from 5/30/2026 7:13:34 AM to 5/31/2026 7:18:34 AM (UTC). Total validity from current time is 86394.861317 seconds\r\n2026-05-30T07:18:39.1390472Z:Validate and Renew Token if necessary finished: 00:00:00.1419952\r\n2026-05-30T07:18:39.2406237Z:Session request is: {\r\n \"expiresAfter\": \"3.00:00:00\",\r\n \"partitionCount\": 0,\r\n \"isProvisionStorage\": true,\r\n \"isLegacyCopsModel\": false,\r\n \"commandName\": \"Sign\",\r\n \"intent\": \"digestsign\",\r\n \"contentType\": \"Bin\",\r\n \"contentOrigin\": \"3rd Party\",\r\n \"productState\": null,\r\n \"audience\": \"External Broad\"\r\n}\r\n2026-05-30T07:18:39.2776882Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:18:39Z] ConfidentialClientApplication 64879470 created\r\n2026-05-30T07:18:39.2776882Z:Gateway Client: Enter AuthenticationProvider, no calls to AAD yet, client id is 0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f, and resource url is https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com, tenant id is 33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:18:39.2857943Z:Gateway Client: a new client and client id is created: 05ddaab5-2d59-46fa-abdd-b27afe0ad951\r\n2026-05-30T07:18:39.3014242Z:Existing Token Acquisition Time: 00:00:00.0053654\r\n2026-05-30T07:18:39.3380568Z:Gateway Client: the client id that is sending a request is: 05ddaab5-2d59-46fa-abdd-b27afe0ad951\r\n2026-05-30T07:18:40.6063774Z:Gateway Client: response send time from cloud gateway: 2026-05-30T07:18:40.4093000+00:00\r\n2026-05-30T07:18:40.6063774Z:Gateway Client: response receive time from client SendAsync: 2026-05-30T07:18:40.6063774+00:00\r\n2026-05-30T07:18:40.6063774Z:Gateway Client: response duration time between cloud gateway and client SendAsync: 00:00:00.1970774\r\n2026-05-30T07:18:40.7344166Z:Session request requestid: 15df4608-76af-4961-8f43-7a411ada8e3b, and submission status is: Pass\r\n2026-05-30T07:18:40.7960350Z:Provision storage complete. Total shards: 100\r\n2026-05-30T07:18:40.8000257Z:Loading DigestSignErrorMappingCache mapping info\r\n2026-05-30T07:18:40.8107905Z:DigestSignErrorMappingCache from server, # of DigestSignOperationErrorPatterns object we get is :: \r\n2026-05-30T07:18:40.8256717Z:Consolidate DigestSignErrorMappingCache, # of DigestSignOperationErrorPatterns object we get is :: 34\r\n2026-05-30T07:18:40.8256717Z:Successfully retrieved policy: policy is {\"policy\":{\"id\":\"0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f-0\",\"workflowExecutionType\":3}}\r\n2026-05-30T07:18:40.8378528Z:Successfully get telemetry connection string: Endpo......\r\n2026-05-30T07:18:40.8395693Z:Warning: \r\n2026-05-30T07:18:40.8432320Z:IAuthInfo constructed: {\r\n \"authenticationType\": \"AAD_CERT\",\r\n \"clientId\": \"0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f\",\r\n \"tenantId\": \"33e01921-4d64-4f8c-a055-5bdaffd5e33d\",\r\n \"aadAuthorityBaseUri\": \"https://login.microsoftonline.com/\",\r\n \"authCert\": {\r\n \"storeLocation\": \"LocalMachine\",\r\n \"storeName\": \"My\",\r\n \"subjectName\": \"CN=0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f.microsoft.com\",\r\n \"sendX5c\": true,\r\n \"withAzureRegion\": false,\r\n \"getCertFromKeyVault\": false,\r\n \"keyVaultName\": null,\r\n \"keyVaultCertName\": null\r\n },\r\n \"requestSigningCert\": {\r\n \"storeLocation\": \"LocalMachine\",\r\n \"storeName\": \"My\",\r\n \"subjectName\": \"CN=0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f\",\r\n \"sendX5c\": false,\r\n \"withAzureRegion\": false,\r\n \"getCertFromKeyVault\": false,\r\n \"keyVaultName\": null,\r\n \"keyVaultCertName\": null\r\n },\r\n \"oAuthToken\": null,\r\n \"version\": \"1.0.0\",\r\n \"esrpClientId\": \"0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f\",\r\n \"federatedTokenData\": null,\r\n \"federatedTokenPath\": null\r\n}\r\n2026-05-30T07:18:40.8448770Z:IPolicyInfo constructed: {\r\n \"intent\": \"digestsign\",\r\n \"contentType\": \"Bin\",\r\n \"contentOrigin\": \"3rd Party\",\r\n \"productState\": null,\r\n \"audience\": \"External Broad\",\r\n \"version\": \"1.0.0\"\r\n}\r\n2026-05-30T07:18:40.8527130Z:IConfigInfo constructed: {\r\n \"esrpApiBaseUri\": \"https://api.esrp.microsoft.com/api/v2/\",\r\n \"esrpSessionTimeoutInSec\": 60,\r\n \"minThreadPoolThreads\": 256,\r\n \"maxDegreeOfParallelism\": 256,\r\n \"exponentialFirstFastRetry\": true,\r\n \"exponentialRetryCount\": 2,\r\n \"exponentialRetryDeltaBackOff\": \"00:00:05\",\r\n \"exponentialRetryMaxBackOff\": \"00:01:00\",\r\n \"exponentialRetryMinBackOff\": \"00:00:03\",\r\n \"appDataFolder\": \"C:\\\\Windows\\\\ServiceProfiles\\\\NetworkService\\\\AppData\\\\Local\",\r\n \"certificateCacheFolder\": null,\r\n \"version\": \"1.0.0\",\r\n \"exitOnFlaggedFile\": false,\r\n \"flaggedFileClientWaitTimeout\": \"1.00:00:00\",\r\n \"servicePointManagerDefaultConnectionLimit\": 256,\r\n \"isOnPremGateway\": false,\r\n \"diagnosticListeners\": null,\r\n \"securityProtocolType\": \"Tls12\",\r\n \"parallelOperationsInFileUploadDownload\": 300,\r\n \"maxTelemetryBuffer\": 200000,\r\n \"telemetryTimeoutInSec\": 0,\r\n \"resourceUri\": \"https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com\",\r\n \"cacheRootFolder\": null,\r\n \"cachedFileTTLInMin\": 7200\r\n}\r\n2026-05-30T07:18:41.1985731Z:Client Telemetry: Xpert agent is not running on the machine. Using events hub for processing client telemetry.\r\n2026-05-30T07:18:41.4140110Z:Key: TotalSignOperationDataCount, Value: 1\r\n2026-05-30T07:18:41.4314995Z:Start Time:5/30/2026 7:18:41 AM, Starting the sign workflow...\r\n2026-05-30T07:18:41.4407625Z:some input info: {\r\n \"contextData\": {\r\n \"build_buildnumber\": \"AzureDevOps_M273_20260511.16\",\r\n \"esrpClientVersion\": \"1.2.142+Branch.master.Sha.90404b43284ec55b3e2251d0e272f8932aa583e2\",\r\n \"userIpAddress\": \"10.0.0.26\",\r\n \"userAgent\": \"at-blueOHG31D\"\r\n },\r\n \"sourceDirectory\": null,\r\n \"sourceLocation\": \"c:\\\\Temp\\\\AzureTemp\\\\spay4ltu.tjd\\\\manifest.cat\",\r\n \"destinationDirectory\": null,\r\n \"destinationLocation\": \"c:\\\\Temp\\\\AzureTemp\\\\spay4ltu.tjd\\\\manifest.cat\",\r\n \"sizeInBytes\": 0,\r\n \"name\": \"manifest.cat\",\r\n \"isSuccess\": null,\r\n \"operationStartedAt\": \"0001-01-01T00:00:00+00:00\",\r\n \"operationEndedAt\": \"0001-01-01T00:00:00+00:00\",\r\n \"operationDurationMS\": 0,\r\n \"processName\": \"EsrpClient\",\r\n \"processId\": 13532,\r\n \"processVersion\": \"1.2.142+Branch.master.Sha.90404b43284ec55b3e2251d0e272f8932aa583e2\",\r\n \"customerCorrelationId\": null,\r\n \"esrpClientSessionGuid\": \"bcd3eec4-b645-4e1b-aedb-4c9525ee35ef\",\r\n \"callerProgram\": \"EsrpClient\",\r\n \"indentity\": \"NT AUTHORITY\\\\NETWORK SERVICE\",\r\n \"clientId\": \"0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f\",\r\n \"exceptionData\": null,\r\n \"apiBaseUrl\": \"https://api.esrp.microsoft.com/api/v2/\",\r\n \"handlerWorkflow\": \"Sign\",\r\n \"submissionRequest\": {\r\n \"contextData\": {\r\n \"build_buildnumber\": \"AzureDevOps_M273_20260511.16\",\r\n \"esrpClientVersion\": \"1.2.142+Branch.master.Sha.90404b43284ec55b3e2251d0e272f8932aa583e2\",\r\n \"userIpAddress\": \"10.0.0.26\",\r\n \"userAgent\": \"at-blueOHG31D\"\r\n },\r\n \"groupId\": null,\r\n \"correlationVector\": \"ed85143f-fbdc-4a45-87b1-b09e31f1e18c\",\r\n \"driEmail\": null,\r\n \"version\": \"1.0.0\"\r\n },\r\n \"policyInfo\": {\r\n \"intent\": \"digestsign\",\r\n \"contentType\": \"Bin\",\r\n \"contentOrigin\": \"3rd Party\",\r\n \"productState\": null,\r\n \"audience\": \"External Broad\",\r\n \"version\": \"1.0.0\"\r\n },\r\n \"osVersion\": \"Microsoft Windows NT 10.0.26100.0\",\r\n \"executingMachineIPAddress\": \"10.0.0.26\"\r\n}\r\n2026-05-30T07:18:41.4920436Z:Executing SignWorkflowType is DigestSignStaticAzure\r\n2026-05-30T07:18:41.5192585Z:Source file \"c:\\Temp\\AzureTemp\\spay4ltu.tjd\\manifest.cat\" hashed in 12 ms\r\n2026-05-30T07:18:41.5764528Z:Thread: 1 - Certificate file mutex \"fOvu+awFsfknUWD4i2p0F05Fn7cYXpAxGZSAj3quKHU=_CP-464321\" acquired (waited 0 ms).\r\nMSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:18:41Z] ConfidentialClientApplication 44123454 created\r\n2026-05-30T07:18:41.5900465Z:Gateway Client: Enter AuthenticationProvider, no calls to AAD yet, client id is 0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f, and resource url is https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com, tenant id is 33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:18:41.5900465Z:Cached token found with name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:18:41.5900465Z:AAD auth caching is in use with this name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:18:41.5918014Z:Global cached token is valid from 5/30/2026 7:13:34 AM to 5/31/2026 7:18:34 AM (UTC). Total validity from current time is 86392.4081986 seconds\r\n2026-05-30T07:18:41.5918014Z:Validate and Renew Token if necessary finished: 00:00:00.0011601\r\nMSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:18:41Z] ConfidentialClientApplication 17230008 created\r\n2026-05-30T07:18:41.5922324Z:Gateway Client: Enter AuthenticationProvider, no calls to AAD yet, client id is 0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f, and resource url is https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com, tenant id is 33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:18:41.5922324Z:Gateway Client: a new client and client id is created: f207c2f3-aba2-4504-a7f1-bd0040797bab\r\n2026-05-30T07:18:41.5922324Z:5/30/2026 7:18:41 AM +00:00:Digest Signing Factory Client type loaded is - [AzureGatewaySubmitter].\r\n2026-05-30T07:18:41.6055246Z:Existing Token Acquisition Time: 00:00:00.0005800\r\n2026-05-30T07:18:41.6857064Z:Gateway Client: the client id that is sending a request is: f207c2f3-aba2-4504-a7f1-bd0040797bab\r\n2026-05-30T07:18:42.1909374Z:Gateway Client: response send time from cloud gateway: 2026-05-30T07:18:42.1419015+00:00\r\n2026-05-30T07:18:42.1909374Z:Gateway Client: response receive time from client SendAsync: 2026-05-30T07:18:42.1909374+00:00\r\n2026-05-30T07:18:42.1909374Z:Gateway Client: response duration time between cloud gateway and client SendAsync: 00:00:00.0490359\r\n2026-05-30T07:18:42.1924534Z:Gateway Client: response time after converting from http response to object: 2026-05-30T07:18:42.1924534+00:00\r\n2026-05-30T07:18:42.3073394Z:Client Telemetry: Events published in this batch: 4\r\n2026-05-30T07:18:42.3073394Z:Client Telemetry: Events received so far in this session: 4\r\n2026-05-30T07:18:42.3073394Z:Client Telemetry: Events published so far in this session: 4\r\n2026-05-30T07:18:42.7082390Z:Existing Token Acquisition Time: 00:00:00.0010054\r\n2026-05-30T07:18:42.7131491Z:Gateway Client: the client id that is sending a request is: f207c2f3-aba2-4504-a7f1-bd0040797bab\r\n2026-05-30T07:18:42.9752406Z:Gateway Client: response send time from cloud gateway: 2026-05-30T07:18:42.9272916+00:00\r\n2026-05-30T07:18:42.9752406Z:Gateway Client: response receive time from client SendAsync: 2026-05-30T07:18:42.9752406+00:00\r\n2026-05-30T07:18:42.9752406Z:Gateway Client: response duration time between cloud gateway and client SendAsync: 00:00:00.0479490\r\n2026-05-30T07:18:42.9850579Z:Gateway Client: response time after converting from http response to object: 2026-05-30T07:18:42.9850579+00:00\r\n2026-05-30T07:18:42.9909759Z:Reading thumbprint from \"C:\\Windows\\ServiceProfiles\\NetworkService\\AppData\\Local\\EsrpClient_bcd3eec4b6454e1baedb4c9525ee35ef\\Certificates\\CP-464321.p7b\" (9692 bytes)...\r\n2026-05-30T07:18:43.0218144Z:Thread: 1 - Wrote new certificate file to \"C:\\Windows\\ServiceProfiles\\NetworkService\\AppData\\Local\\EsrpClient_bcd3eec4b6454e1baedb4c9525ee35ef\\Certificates\\CP-464321.p7b\".\r\n2026-05-30T07:18:43.0218144Z:Thread: 1 - Certificate file mutex \"fOvu+awFsfknUWD4i2p0F05Fn7cYXpAxGZSAj3quKHU=_CP-464321\" released (held for 1445 ms).\r\n2026-05-30T07:18:43.0232439Z:digest sign request expire time is: 5/30/2026 7:19:40 AM\r\n2026-05-30T07:18:46.5446700Z:Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe sign /NPH /fd \"SHA256\" /f \"C:\\Windows\\ServiceProfiles\\NetworkService\\AppData\\Local\\EsrpClient_bcd3eec4b6454e1baedb4c9525ee35ef\\Certificates\\CP-464321.p7b\" /sha1 \"464FA2A9D4A43EA5C3E53CE4562B501CB6EFA47F\" /du \"https://www.1eswiki.com/wiki/ADO_Manifest_Generator\" /d \"Packaging SSSC Codesign - DigestSign\" /tr \"http://aztss.trafficmanager.net/TSS/HttpTspServer\" /td sha256 /dlib \"c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\EsrpClient.Sign.DigestSignLib.dll\" /dmdf \"C:\\Windows\\ServiceProfiles\\NetworkService\\AppData\\Local\\EsrpClient_bcd3eec4b6454e1baedb4c9525ee35ef\\838CA605893F4AB687770BA6F2E2911B.json\" \"c:\\Temp\\AzureTemp\\spay4ltu.tjd\\manifest.cat\" completed in 3517 ms\r\nExit Code: 0\r\nStdOut:\r\nDone Adding Additional Store\r\n\r\nESRP Digest Signing\r\n\r\n2026-05-30T07:18:44.1444403Z:Digest Signer : SecurityProtocolType used from the metadata info is : Tls12\r\nMSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:18:44Z] ConfidentialClientApplication 654897 created\r\n2026-05-30T07:18:44.2952589Z:Cached token found with name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:18:44.2969564Z:AAD auth caching is in use with this name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:18:44.4357953Z:Global cached token is valid from 5/30/2026 7:13:34 AM to 5/31/2026 7:18:34 AM (UTC). Total validity from current time is 86389.5653318 seconds\r\n2026-05-30T07:18:44.4357953Z:Validate and Renew Token if necessary finished: 00:00:00.1379837\r\nMSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:18:44Z] ConfidentialClientApplication 49044892 created\r\n2026-05-30T07:18:44.5286629Z:Existing Token Acquisition Time: 00:00:00.0046570\r\n2026-05-30T07:18:45.3525527Z:Gateway Submission Request Signing Time: 00:00:00.1142666\r\n2026-05-30T07:18:45.3525527Z:Gateway Submission Request Send Time: 00:00:00.6877076\r\n2026-05-30T07:18:45.3525527Z:Gateway Submission Overall Submission Time: 00:00:00.8204397\r\n2026-05-30T07:18:45.3525527Z:Operation ID: 2bbc5224-c6b8-4611-89b6-14fbac66753e\r\n2026-05-30T07:18:45.8535022Z:Gateway Status Delay Time: 00:00:00.5012467\r\n2026-05-30T07:18:45.8710131Z:Existing Token Acquisition Time: 00:00:00.0010330\r\n2026-05-30T07:18:45.9335512Z:Gateway Status Request Send Time: 00:00:00.0580240\r\n2026-05-30T07:18:45.9341310Z:Gateway Status Overall Time: 00:00:00.0605279\r\n2026-05-30T07:18:45.9417417Z:Gateway Submission Duration: 1205\r\n2026-05-30T07:18:45.9422776Z:Gateway Submission Attempts: 1\r\n2026-05-30T07:18:45.9422776Z:Gateway GetStatus Duration: 80\r\n2026-05-30T07:18:45.9422776Z:Gateway GetStatus Attempts: 1\r\n2026-05-30T07:18:45.9422776Z:$$5a9f5111cf8f42a0a0edc419862b95dc##_ThrottleCount: 0\r\n2026-05-30T07:18:45.9422776Z:$$65265a48891640eb86e148d3c9a627fa##_ThrottledTimeInSec: 0\r\n2026-05-30T07:18:45.9422776Z:Service Call: 1964.2417 ms\r\nSuccessfully signed: c:\\Temp\\AzureTemp\\spay4ltu.tjd\\manifest.cat\r\n\r\n\r\n\r\nStdErr:\r\n\r\n\r\n\r\n\r\n2026-05-30T07:18:46.7469175Z:Warning: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\spay4ltu.tjd\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\spay4ltu.tjd\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n2026-05-30T07:18:46.7488248Z:Warning: Warning: Operation Error (1) - Operation: signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\spay4ltu.tjd\\manifest.cat\" - Retrying in 500 ms...\r\n2026-05-30T07:18:46.8559552Z:Client Telemetry: Events published in this batch: 2\r\n2026-05-30T07:18:46.8559552Z:Client Telemetry: Events received so far in this session: 6\r\n2026-05-30T07:18:46.8559552Z:Client Telemetry: Events published so far in this session: 6\r\n2026-05-30T07:18:46.8939602Z:Warning: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\spay4ltu.tjd\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\spay4ltu.tjd\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n2026-05-30T07:18:46.8946195Z:Warning: Warning: Operation Error (2) - Operation: signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\spay4ltu.tjd\\manifest.cat\" - Retrying in 500 ms...\r\n2026-05-30T07:18:47.3669048Z:Client Telemetry: Events published in this batch: 1\r\n2026-05-30T07:18:47.3669048Z:Client Telemetry: Events received so far in this session: 7\r\n2026-05-30T07:18:47.3669048Z:Client Telemetry: Events published so far in this session: 7\r\n2026-05-30T07:18:47.5190977Z:Warning: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\spay4ltu.tjd\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\spay4ltu.tjd\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n2026-05-30T07:18:47.5554625Z:\r\n2026-05-30T07:18:47.5568549Z:Error: System.AggregateException: One or more errors occurred. ---> MS.Ess.EsrpClient.Sign.Exceptions.EsrpDigestSignExecuteProcessFailedException: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\spay4ltu.tjd\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\spay4ltu.tjd\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass27_0.<ExecuteDigestSignOperation>b__1()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.<>c__DisplayClass1.<ExecuteAction>b__0()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.ExecuteAction[TResult](Func`1 func)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.ExecuteDigestSignOperation(Input digestSignInput, Operation digestSignOperation, SignOperationData fileEntry, SignStatusResponse completionResponse, SignCommandDefinition param, Guid correlationId, String correlationVector, CancellationToken cancellationToken, String requestId, String tmpDestinationLocation)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.DigestSignInternal(Input digestSignInput, SignCommandDefinition param, Guid& correlationId, String correlationVector, CancellationToken cancellationToken, String groupId, SignOperationData fileEntry, Int32& signedFileCount, Int32& certRefreshCount, ConcurrentBag`1 completionResponses, RetryPolicy`1 operationRetryPolicy, String dynamicCertificateFile, String dynamicCertificateThumbprint, String requestId)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass23_3.<DigestSignAsync>b__6(SignOperationData fileEntry)\r\n --- End of inner exception stack trace ---\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<DigestSignAsync>d__23.MoveNext()\r\n---> (Inner Exception #0) MS.Ess.EsrpClient.Sign.Exceptions.EsrpDigestSignExecuteProcessFailedException: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\spay4ltu.tjd\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\spay4ltu.tjd\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass27_0.<ExecuteDigestSignOperation>b__1()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.<>c__DisplayClass1.<ExecuteAction>b__0()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.ExecuteAction[TResult](Func`1 func)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.ExecuteDigestSignOperation(Input digestSignInput, Operation digestSignOperation, SignOperationData fileEntry, SignStatusResponse completionResponse, SignCommandDefinition param, Guid correlationId, String correlationVector, CancellationToken cancellationToken, String requestId, String tmpDestinationLocation)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.DigestSignInternal(Input digestSignInput, SignCommandDefinition param, Guid& correlationId, String correlationVector, CancellationToken cancellationToken, String groupId, SignOperationData fileEntry, Int32& signedFileCount, Int32& certRefreshCount, ConcurrentBag`1 completionResponses, RetryPolicy`1 operationRetryPolicy, String dynamicCertificateFile, String dynamicCertificateThumbprint, String requestId)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass23_3.<DigestSignAsync>b__6(SignOperationData fileEntry)<---\r\n\r\n2026-05-30T07:18:47.6233837Z:Constructing EsrpClientResponse\r\n2026-05-30T07:18:47.6246026Z:EsrpClientResponse is constructed\r\n2026-05-30T07:18:47.6599711Z:OperationDurationMs: 6141\r\n2026-05-30T07:18:47.6599711Z:DynamicCertGenerationTimeMs: 0\r\n2026-05-30T07:18:47.6599711Z:TimeToGetStorageShradsMs: 0\r\n2026-05-30T07:18:47.6599711Z:TotalScanSubmissionTimeMs: 0\r\n2026-05-30T07:18:47.6599711Z:ThrottleCount: 0\r\n2026-05-30T07:18:47.6599711Z:ThrottledTimeInSec: 0\r\n2026-05-30T07:18:47.6599711Z:\r\n2026-05-30T07:18:47.6599711Z:0/1 files signed in 00:00:06.141 (0 files/s, total number of certs refreshed: 0)\r\n2026-05-30T07:18:47.6629329Z:result json is: {\r\n \"submissionResponses\": [\r\n {\r\n \"fileStatusDetail\": [\r\n {\r\n \"sourceHash\": \"0F+OwjUaFS3BzWSDqLcoFQThtcHY+0EKw5lpzZ9+qZo=\",\r\n \"hashType\": \"sha256\",\r\n \"destinationHash\": null,\r\n \"certificateThumbprint\": null,\r\n \"destinationLocation\": \"c:\\\\Temp\\\\AzureTemp\\\\spay4ltu.tjd\\\\manifest.cat\",\r\n \"destinationFileSizeInBytes\": 0,\r\n \"sourceLocation\": \"c:\\\\Temp\\\\AzureTemp\\\\spay4ltu.tjd\\\\manifest.cat\"\r\n }\r\n ],\r\n \"operationId\": \"2bbc5224-c6b8-4611-89b6-14fbac66753e\",\r\n \"customerCorrelationId\": \"ed85143f-fbdc-4a45-87b1-b09e31f1e18c\",\r\n \"statusCode\": \"failCanRetry\",\r\n \"errorInfo\": {\r\n \"code\": \"3138\",\r\n \"details\": {\r\n \"operation\": \"c:\\\\AT\\\\sitesroot\\\\0\\\\bin\\\\Plugins\\\\ESRPClient\\\\Win10.x86\\\\signtool.exe verify /pa /tw \\\"c:\\\\Temp\\\\AzureTemp\\\\spay4ltu.tjd\\\\manifest.cat\\\"\",\r\n \"exitCode\": \"1\",\r\n \"stdOut\": \"File: c:\\\\Temp\\\\AzureTemp\\\\spay4ltu.tjd\\\\manifest.cat\\r\\nIndex Algorithm Timestamp \\r\\n========================================\\r\\n\\r\\nNumber of errors: 1\\r\\n\\r\\n\\r\\n\",\r\n \"stdErr\": \"SignTool Error: A certificate chain processed, but terminated in a root\\r\\n\\tcertificate which is not trusted by the trust provider.\\r\\n\\r\\n\"\r\n },\r\n \"innerError\": null\r\n }\r\n }\r\n ],\r\n \"esrpClientSessionGuid\": \"bcd3eec4-b645-4e1b-aedb-4c9525ee35ef\",\r\n \"version\": \"1.0.0\"\r\n}\r\n2026-05-30T07:18:47.7031804Z:Warning: \r\n\r\n2026-05-30T07:18:47.7031804Z:esrp-client-finalTime: 9958.5603\r\n2026-05-30T07:18:47.7031804Z:Final return code is: 1\r\n2026-05-30T07:18:47.7031804Z:ClientTelemetryDispose started, this is not affecting Exe reliability, only lost telemetry if error happens ***********************\r\n2026-05-30T07:18:47.7074172Z:Client Telemetry: Flushing telemetry buffer with timeout 0 ms\r\n2026-05-30T07:18:47.7110084Z:Client Telemetry: Flushing telemetry buffer completed within timeout (true/false): False\r\n2026-05-30T07:18:47.7110084Z:Client Telemetry: Disposing telemetry buffer and event hub client\r\n2026-05-30T07:18:47.7159061Z:Client Telemetry: Events published in this batch: 4\r\n2026-05-30T07:18:47.7159061Z:Client Telemetry: Events received so far in this session: 11\r\n2026-05-30T07:18:47.7159061Z:Client Telemetry: Events published so far in this session: 11\r\n2026-05-30T07:18:47.7943466Z:event lost number: 0\r\n2026-05-30T07:18:47.7943466Z:total event processed number: 11\r\n2026-05-30T07:18:47.7943466Z:total event received number: 11\r\n2026-05-30T07:18:47.7943466Z:##EsrpClientTelemetry.TotalEventsProcessed##: 11\r\n2026-05-30T07:18:47.7943466Z:##EsrpClientTelemetry.TotalEventsReceived##: 11\r\n2026-05-30T07:18:47.7943466Z:##EsrpClientTelemetry.DisposeTimeMilliseconds##: 90\r\n2026-05-30T07:18:47.7943466Z:ClientTelemetryDispose ended ***********************\r\n","StdErr":"2026-05-30T07:18:47.5568549Z:Error: System.AggregateException: One or more errors occurred. ---> MS.Ess.EsrpClient.Sign.Exceptions.EsrpDigestSignExecuteProcessFailedException: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\spay4ltu.tjd\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\spay4ltu.tjd\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass27_0.<ExecuteDigestSignOperation>b__1()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.<>c__DisplayClass1.<ExecuteAction>b__0()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.ExecuteAction[TResult](Func`1 func)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.ExecuteDigestSignOperation(Input digestSignInput, Operation digestSignOperation, SignOperationData fileEntry, SignStatusResponse completionResponse, SignCommandDefinition param, Guid correlationId, String correlationVector, CancellationToken cancellationToken, String requestId, String tmpDestinationLocation)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.DigestSignInternal(Input digestSignInput, SignCommandDefinition param, Guid& correlationId, String correlationVector, CancellationToken cancellationToken, String groupId, SignOperationData fileEntry, Int32& signedFileCount, Int32& certRefreshCount, ConcurrentBag`1 completionResponses, RetryPolicy`1 operationRetryPolicy, String dynamicCertificateFile, String dynamicCertificateThumbprint, String requestId)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass23_3.<DigestSignAsync>b__6(SignOperationData fileEntry)\r\n --- End of inner exception stack trace ---\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<DigestSignAsync>d__23.MoveNext()\r\n---> (Inner Exception #0) MS.Ess.EsrpClient.Sign.Exceptions.EsrpDigestSignExecuteProcessFailedException: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\spay4ltu.tjd\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\spay4ltu.tjd\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass27_0.<ExecuteDigestSignOperation>b__1()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.<>c__DisplayClass1.<ExecuteAction>b__0()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.ExecuteAction[TResult](Func`1 func)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.ExecuteDigestSignOperation(Input digestSignInput, Operation digestSignOperation, SignOperationData fileEntry, SignStatusResponse completionResponse, SignCommandDefinition param, Guid correlationId, String correlationVector, CancellationToken cancellationToken, String requestId, String tmpDestinationLocation)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.DigestSignInternal(Input digestSignInput, SignCommandDefinition param, Guid& correlationId, String correlationVector, CancellationToken cancellationToken, String groupId, SignOperationData fileEntry, Int32& signedFileCount, Int32& certRefreshCount, ConcurrentBag`1 completionResponses, RetryPolicy`1 operationRetryPolicy, String dynamicCertificateFile, String dynamicCertificateThumbprint, String requestId)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass23_3.<DigestSignAsync>b__6(SignOperationData fileEntry)<---\r\n\r\n","ExitCode":1,"RunningTime":"00:00:10.8712754"} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
| {"Source":"InternalBuild","Data":{"System.CollectionId":"cb55739e-4afe-46a3-970f-1b49d8ee7564","System.DefinitionId":"190018","System.TeamProjectId":"c93e867a-8815-43c1-92c4-e7dd5404f1e1","System.TeamProject":"Dart","Build.BuildId":"148237282","Build.BuildNumber":"v0.6.0_20260530.1","Build.DefinitionName":"MXC-Official-Build","Build.DefinitionRevision":"7","Build.Repository.Name":"microsoft/mxc","Build.Repository.Provider":"GitHub","Build.Repository.Id":"microsoft/mxc","Build.SourceBranch":"refs/tags/v0.6.0","Build.SourceBranchName":"v0.6.0","Build.SourceVersion":"206dd5475c80b127fc92a4a38d49f83e77d054d5","Build.Repository.Uri":"https://github.com/microsoft/mxc","EbomId":"b2c308dc-78a8-545a-af13-30ca09182285","1ES.PT.TemplateType":"official"},"Feed":null} |
| {"StdOut":"******************************************************************************\r\nMachine Information\r\nMachine Name: at-blue8CGPNC\r\nMachine Ip: 10.0.0.128\r\nOperating System: Microsoft Windows NT 10.0.26100.0\r\nUser Name: at-blue8CGPNC$\r\nProcessor Count: 64\r\nProcess Name: EsrpClient\r\nProcess Id: 25644\r\nCaller Program: EsrpClient.exe\r\nIdentity: NT AUTHORITY\\NETWORK SERVICE\r\nProcess Version: 1.2.142+Branch.master.Sha.90404b43284ec55b3e2251d0e272f8932aa583e2\r\nProcess Bitness: 64 bit\r\n******************************************************************************\r\nCommandline received: Sign | -a | c:\\Temp\\AzureTemp\\TFSTemp\\vctmp8524_983026.json | -p | c:\\Temp\\AzureTemp\\TFSTemp\\vctmp8524_914994.json | -c | c:\\Temp\\AzureTemp\\TFSTemp\\vctmp8524_647041.json | -i | c:\\Temp\\AzureTemp\\TFSTemp\\vctmp14544_903722.json | -o | c:\\Temp\\AzureTemp\\TFSTemp\\v4971D4.tmp | -l | Verbose ESRP session Id is: 938c323b-2347-49a7-b422-4d9bb029f850\r\n2026-05-30T07:12:50.9251005Z:Command you are trying to use is: Sign\r\n2026-05-30T07:12:50.9318972Z:Correlation Vector for this run is: c3556c28-2050-446d-849b-49f8eae12585\r\n2026-05-30T07:12:50.9318972Z:Groupid for this run is empty\r\n2026-05-30T07:12:51.0465451Z:request signing cert being used is this thumbprint: 42661F29AF78973B30992C76D622575F555E3D84 in store LocalMachine\\My\r\n2026-05-30T07:12:51.0465451Z:Both certificates validation passed.\r\n2026-05-30T07:12:51.0485492Z:The auth cert we choose to use, subject name is: CN=0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f.microsoft.com, thumbprint is: 943507930F1390CF64FC5E9DB45F03091556EEBE\r\n2026-05-30T07:12:51.1044650Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:51Z] ConfidentialClientApplication 37368736 created\r\n2026-05-30T07:12:51.1052321Z:Gateway Client: Enter AuthenticationProvider, no calls to AAD yet, client id is 0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f, and resource url is https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com, tenant id is 33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:51.1105858Z:There is no memory mapped file accociated with this name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:51.1124689Z:AAD auth caching is in use with this name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:51.1227599Z:Validate and Renew Token if necessary finished: 00:00:00.0080450\r\n2026-05-30T07:12:51.2155959Z:Session request is: {\r\n \"expiresAfter\": \"3.00:00:00\",\r\n \"partitionCount\": 0,\r\n \"isProvisionStorage\": true,\r\n \"isLegacyCopsModel\": false,\r\n \"commandName\": \"Sign\",\r\n \"intent\": \"digestsign\",\r\n \"contentType\": \"Bin\",\r\n \"contentOrigin\": \"3rd Party\",\r\n \"productState\": null,\r\n \"audience\": \"External Broad\"\r\n}\r\n2026-05-30T07:12:51.2473449Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:51Z] ConfidentialClientApplication 29578451 created\r\n2026-05-30T07:12:51.2473449Z:Gateway Client: Enter AuthenticationProvider, no calls to AAD yet, client id is 0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f, and resource url is https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com, tenant id is 33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:51.2547294Z:Gateway Client: a new client and client id is created: 7ee49807-067f-4721-80a5-f2ee2fb3e00e\r\n2026-05-30T07:12:51.2925185Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:51Z - 11031644-1340-456a-95c1-ba6f3a591ac1] MSAL MSAL.Desktop with assembly version '4.70.0.0'. CorrelationId(11031644-1340-456a-95c1-ba6f3a591ac1)\r\n2026-05-30T07:12:51.3064947Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:51Z - 11031644-1340-456a-95c1-ba6f3a591ac1] === AcquireTokenForClientParameters ===\r\nSendX5C: True\r\nForceRefresh: False\r\nAccessTokenHashToRefresh: False\r\n\r\n2026-05-30T07:12:51.3195333Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:51Z - 11031644-1340-456a-95c1-ba6f3a591ac1] \r\n=== Request Data ===\r\nAuthority Provided? - True\r\nScopes - https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com/.default\r\nExtra Query Params Keys (space separated) - \r\nApiId - AcquireTokenForClient\r\nIsConfidentialClient - True\r\nSendX5C - True\r\nLoginHint ? False\r\nIsBrokerConfigured - False\r\nHomeAccountId - False\r\nCorrelationId - 11031644-1340-456a-95c1-ba6f3a591ac1\r\nUserAssertion set: False\r\nLongRunningOboCacheKey set: False\r\nRegion configured: \r\n\r\n2026-05-30T07:12:51.3203300Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:51Z - 11031644-1340-456a-95c1-ba6f3a591ac1] === Token Acquisition (ClientCredentialRequest) started:\r\n\t Scopes: https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com/.default\r\n\tAuthority Host: login.microsoftonline.com\r\n2026-05-30T07:12:51.3372101Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:51Z - 11031644-1340-456a-95c1-ba6f3a591ac1] [Internal cache] Total number of cache partitions found while getting access tokens: 0\r\n2026-05-30T07:12:51.3372101Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:51Z - 11031644-1340-456a-95c1-ba6f3a591ac1] [FindAccessTokenAsync] Discovered 0 access tokens in cache using partition key: 0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d_AppTokenCache\r\n2026-05-30T07:12:51.3372101Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:51Z - 11031644-1340-456a-95c1-ba6f3a591ac1] [FindAccessTokenAsync] No access tokens found in the cache. Skipping filtering. \r\n2026-05-30T07:12:51.3484097Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:51Z - 11031644-1340-456a-95c1-ba6f3a591ac1] [Instance Discovery] Instance discovery is enabled and will be performed\r\n2026-05-30T07:12:51.3527711Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:51Z - 11031644-1340-456a-95c1-ba6f3a591ac1] [Region discovery] WithAzureRegion not configured. \r\n2026-05-30T07:12:51.3527711Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:51Z - 11031644-1340-456a-95c1-ba6f3a591ac1] [Region discovery] Not using a regional authority. \r\n2026-05-30T07:12:51.3567872Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:51Z - 11031644-1340-456a-95c1-ba6f3a591ac1] [Instance Discovery] Tried to use network cache provider for login.microsoftonline.com. Success? False. \r\n2026-05-30T07:12:51.3668266Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:51Z - 11031644-1340-456a-95c1-ba6f3a591ac1] Fetching instance discovery from the network from host login.microsoftonline.com. \r\n2026-05-30T07:12:51.3859061Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:51Z - 11031644-1340-456a-95c1-ba6f3a591ac1] Starting [Oauth2Client] Sending GET request \r\n2026-05-30T07:12:51.3896726Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:51Z - 11031644-1340-456a-95c1-ba6f3a591ac1] Starting [HttpManager] ExecuteAsync\r\n2026-05-30T07:12:51.3976947Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:51Z - 11031644-1340-456a-95c1-ba6f3a591ac1] [HttpManager] Sending request. Method: GET. Host: https://login.microsoftonline.com. Binding Certificate: False \r\n2026-05-30T07:12:51.8808678Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:51Z - 11031644-1340-456a-95c1-ba6f3a591ac1] [HttpManager] Received response. Status code: OK. \r\n2026-05-30T07:12:51.8935318Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:51Z - 11031644-1340-456a-95c1-ba6f3a591ac1] Finished [HttpManager] ExecuteAsync in 503 ms\r\n2026-05-30T07:12:51.8939528Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:51Z - 11031644-1340-456a-95c1-ba6f3a591ac1] Finished [Oauth2Client] Sending GET request in 507 ms\r\n2026-05-30T07:12:51.9059308Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:51Z - 11031644-1340-456a-95c1-ba6f3a591ac1] Starting [OAuth2Client] Deserializing response\r\n2026-05-30T07:12:52.0320389Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] Finished [OAuth2Client] Deserializing response in 126 ms\r\n2026-05-30T07:12:52.0325108Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] [Instance Discovery] Tried to use network cache provider for login.microsoftonline.com. Success? True. \r\n2026-05-30T07:12:52.0325108Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] [Instance Discovery] After hitting the discovery endpoint, the network provider found an entry for login.microsoftonline.com ? True. \r\n2026-05-30T07:12:52.0369841Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] Authority validation enabled? True. \r\n2026-05-30T07:12:52.0380550Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] Authority validation - is known env? True. \r\n2026-05-30T07:12:52.0559998Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] Starting TokenClient:SendTokenRequestAsync\r\n2026-05-30T07:12:52.0599464Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] [TokenClient] Before adding the client assertion / secret\r\n2026-05-30T07:12:52.0616134Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] Building assertion from certificate with clientId: 0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f at endpoint: https://login.microsoftonline.com/33e01921-4d64-4f8c-a055-5bdaffd5e33d/oauth2/v2.0/token\r\n2026-05-30T07:12:52.0616134Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] Proceeding with JWT token creation and adding client assertion.\r\n2026-05-30T07:12:52.0958949Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] [TokenClient] After adding the client assertion / secret\r\n2026-05-30T07:12:52.1053608Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] [Token Client] Fetching MsalTokenResponse .... \r\n2026-05-30T07:12:52.1053608Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] Starting [Oauth2Client] Sending POST request \r\n2026-05-30T07:12:52.1117553Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] Starting [HttpManager] ExecuteAsync\r\n2026-05-30T07:12:52.1117553Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] [HttpManager] Sending request. Method: POST. Host: https://login.microsoftonline.com. Binding Certificate: False \r\n2026-05-30T07:12:52.2922012Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] [HttpManager] Received response. Status code: OK. \r\n2026-05-30T07:12:52.2922012Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] Finished [HttpManager] ExecuteAsync in 180 ms\r\n2026-05-30T07:12:52.2922012Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] Finished [Oauth2Client] Sending POST request in 186 ms\r\n2026-05-30T07:12:52.2922012Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] Starting [OAuth2Client] Deserializing response\r\n2026-05-30T07:12:52.3060111Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] Finished [OAuth2Client] Deserializing response in 13 ms\r\n2026-05-30T07:12:52.3060111Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] ScopeSet was missing from the token response, so using developer provided scopes in the result. \r\n2026-05-30T07:12:52.3060111Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] Finished TokenClient:SendTokenRequestAsync in 250 ms\r\n2026-05-30T07:12:52.3080694Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] Checking client info returned from the server..\r\n2026-05-30T07:12:52.3080694Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] Saving token response to cache..\r\n2026-05-30T07:12:52.3179423Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] \r\n[MsalTokenResponse]\r\nError: \r\nErrorDescription: \r\nScopes: https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com/.default\r\nExpiresIn: 86399\r\nRefreshIn: 43199\r\nAccessToken returned: True\r\nAccessToken Type: Bearer\r\nRefreshToken returned: False\r\nIdToken returned: False\r\nClientInfo returned: False\r\nFamilyId: \r\nWamAccountId exists: False\r\n2026-05-30T07:12:52.3179423Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] [SaveTokenResponseAsync] ID Token not present in response. \r\n2026-05-30T07:12:52.3200046Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] Cannot determine home account ID - or id token or no client info and no subject \r\n2026-05-30T07:12:52.3255562Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] [SaveTokenResponseAsync] Entering token cache semaphore. Count Real semaphore: True. Count: 1.\r\n2026-05-30T07:12:52.3260272Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] [SaveTokenResponseAsync] Entered token cache semaphore. \r\n2026-05-30T07:12:52.3260272Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] [SaveTokenResponseAsync] Saving AT in cache and removing overlapping ATs...\r\n2026-05-30T07:12:52.3282447Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] Looking for scopes for the authority in the cache which intersect with https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com/.default\r\n2026-05-30T07:12:52.3282447Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z] [Internal cache] Total number of cache partitions found while getting access tokens: 0\r\n2026-05-30T07:12:52.3282447Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] Intersecting scope entries count - 0\r\n2026-05-30T07:12:52.3288760Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] Not saving to ADAL legacy cache. \r\n2026-05-30T07:12:52.3288760Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] [SaveTokenResponseAsync] Released token cache semaphore. \r\n2026-05-30T07:12:52.3334622Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] \r\n\t=== Token Acquisition finished successfully:\r\n2026-05-30T07:12:52.3349797Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] AT expiration time: 5/31/2026 7:12:51 AM +00:00, scopes: https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com/.default. source: IdentityProvider\r\n2026-05-30T07:12:52.3349797Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] Fetched access token from host login.microsoftonline.com. \r\n2026-05-30T07:12:52.3357657Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] \r\n[LogMetricsFromAuthResult] Cache Refresh Reason: NoCachedAccessToken\r\n[LogMetricsFromAuthResult] DurationInCacheInMs: 0\r\n[LogMetricsFromAuthResult] DurationTotalInMs: 1022\r\n[LogMetricsFromAuthResult] DurationInHttpInMs: 662\r\n2026-05-30T07:12:52.3369838Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:52Z - 11031644-1340-456a-95c1-ba6f3a591ac1] TokenEndpoint: ****\r\n2026-05-30T07:12:52.3511331Z:Use AAD token. CERT\r\n2026-05-30T07:12:52.3694273Z:New Token Acquisition Time: 00:00:01.1049217\r\n2026-05-30T07:12:52.4075780Z:Gateway Client: the client id that is sending a request is: 7ee49807-067f-4721-80a5-f2ee2fb3e00e\r\n2026-05-30T07:12:53.6618307Z:Gateway Client: response send time from cloud gateway: 2026-05-30T07:12:53.4132619+00:00\r\n2026-05-30T07:12:53.6618307Z:Gateway Client: response receive time from client SendAsync: 2026-05-30T07:12:53.6618307+00:00\r\n2026-05-30T07:12:53.6618307Z:Gateway Client: response duration time between cloud gateway and client SendAsync: 00:00:00.2485688\r\n2026-05-30T07:12:53.7633441Z:Session request requestid: 910e7eae-325b-412f-9fff-693486810b9e, and submission status is: Pass\r\n2026-05-30T07:12:53.8169572Z:Provision storage complete. Total shards: 100\r\n2026-05-30T07:12:53.8209668Z:Loading DigestSignErrorMappingCache mapping info\r\n2026-05-30T07:12:53.8304925Z:DigestSignErrorMappingCache from server, # of DigestSignOperationErrorPatterns object we get is :: \r\n2026-05-30T07:12:53.8568412Z:Consolidate DigestSignErrorMappingCache, # of DigestSignOperationErrorPatterns object we get is :: 34\r\n2026-05-30T07:12:53.8568412Z:Successfully retrieved policy: policy is {\"policy\":{\"id\":\"0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f-0\",\"workflowExecutionType\":3}}\r\n2026-05-30T07:12:53.8674169Z:Successfully get telemetry connection string: Endpo......\r\n2026-05-30T07:12:53.8684791Z:Warning: \r\n2026-05-30T07:12:53.8717640Z:IAuthInfo constructed: {\r\n \"authenticationType\": \"AAD_CERT\",\r\n \"clientId\": \"0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f\",\r\n \"tenantId\": \"33e01921-4d64-4f8c-a055-5bdaffd5e33d\",\r\n \"aadAuthorityBaseUri\": \"https://login.microsoftonline.com/\",\r\n \"authCert\": {\r\n \"storeLocation\": \"LocalMachine\",\r\n \"storeName\": \"My\",\r\n \"subjectName\": \"CN=0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f.microsoft.com\",\r\n \"sendX5c\": true,\r\n \"withAzureRegion\": false,\r\n \"getCertFromKeyVault\": false,\r\n \"keyVaultName\": null,\r\n \"keyVaultCertName\": null\r\n },\r\n \"requestSigningCert\": {\r\n \"storeLocation\": \"LocalMachine\",\r\n \"storeName\": \"My\",\r\n \"subjectName\": \"CN=0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f\",\r\n \"sendX5c\": false,\r\n \"withAzureRegion\": false,\r\n \"getCertFromKeyVault\": false,\r\n \"keyVaultName\": null,\r\n \"keyVaultCertName\": null\r\n },\r\n \"oAuthToken\": null,\r\n \"version\": \"1.0.0\",\r\n \"esrpClientId\": \"0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f\",\r\n \"federatedTokenData\": null,\r\n \"federatedTokenPath\": null\r\n}\r\n2026-05-30T07:12:53.8723257Z:IPolicyInfo constructed: {\r\n \"intent\": \"digestsign\",\r\n \"contentType\": \"Bin\",\r\n \"contentOrigin\": \"3rd Party\",\r\n \"productState\": null,\r\n \"audience\": \"External Broad\",\r\n \"version\": \"1.0.0\"\r\n}\r\n2026-05-30T07:12:53.8762771Z:IConfigInfo constructed: {\r\n \"esrpApiBaseUri\": \"https://api.esrp.microsoft.com/api/v2/\",\r\n \"esrpSessionTimeoutInSec\": 60,\r\n \"minThreadPoolThreads\": 256,\r\n \"maxDegreeOfParallelism\": 256,\r\n \"exponentialFirstFastRetry\": true,\r\n \"exponentialRetryCount\": 2,\r\n \"exponentialRetryDeltaBackOff\": \"00:00:05\",\r\n \"exponentialRetryMaxBackOff\": \"00:01:00\",\r\n \"exponentialRetryMinBackOff\": \"00:00:03\",\r\n \"appDataFolder\": \"C:\\\\Windows\\\\ServiceProfiles\\\\NetworkService\\\\AppData\\\\Local\",\r\n \"certificateCacheFolder\": null,\r\n \"version\": \"1.0.0\",\r\n \"exitOnFlaggedFile\": false,\r\n \"flaggedFileClientWaitTimeout\": \"1.00:00:00\",\r\n \"servicePointManagerDefaultConnectionLimit\": 256,\r\n \"isOnPremGateway\": false,\r\n \"diagnosticListeners\": null,\r\n \"securityProtocolType\": \"Tls12\",\r\n \"parallelOperationsInFileUploadDownload\": 300,\r\n \"maxTelemetryBuffer\": 200000,\r\n \"telemetryTimeoutInSec\": 0,\r\n \"resourceUri\": \"https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com\",\r\n \"cacheRootFolder\": null,\r\n \"cachedFileTTLInMin\": 7200\r\n}\r\n2026-05-30T07:12:54.1206966Z:Client Telemetry: Xpert agent is not running on the machine. Using events hub for processing client telemetry.\r\n2026-05-30T07:12:54.3134093Z:Key: TotalSignOperationDataCount, Value: 1\r\n2026-05-30T07:12:54.3287067Z:Start Time:5/30/2026 7:12:54 AM, Starting the sign workflow...\r\n2026-05-30T07:12:54.3404193Z:some input info: {\r\n \"contextData\": {\r\n \"build_buildnumber\": \"AzureDevOps_M273_20260511.16\",\r\n \"esrpClientVersion\": \"1.2.142+Branch.master.Sha.90404b43284ec55b3e2251d0e272f8932aa583e2\",\r\n \"userIpAddress\": \"10.0.0.128\",\r\n \"userAgent\": \"at-blue8CGPNC\"\r\n },\r\n \"sourceDirectory\": null,\r\n \"sourceLocation\": \"c:\\\\Temp\\\\AzureTemp\\\\lsdu0yn5.c2d\\\\manifest.cat\",\r\n \"destinationDirectory\": null,\r\n \"destinationLocation\": \"c:\\\\Temp\\\\AzureTemp\\\\lsdu0yn5.c2d\\\\manifest.cat\",\r\n \"sizeInBytes\": 0,\r\n \"name\": \"manifest.cat\",\r\n \"isSuccess\": null,\r\n \"operationStartedAt\": \"0001-01-01T00:00:00+00:00\",\r\n \"operationEndedAt\": \"0001-01-01T00:00:00+00:00\",\r\n \"operationDurationMS\": 0,\r\n \"processName\": \"EsrpClient\",\r\n \"processId\": 25644,\r\n \"processVersion\": \"1.2.142+Branch.master.Sha.90404b43284ec55b3e2251d0e272f8932aa583e2\",\r\n \"customerCorrelationId\": null,\r\n \"esrpClientSessionGuid\": \"938c323b-2347-49a7-b422-4d9bb029f850\",\r\n \"callerProgram\": \"EsrpClient\",\r\n \"indentity\": \"NT AUTHORITY\\\\NETWORK SERVICE\",\r\n \"clientId\": \"0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f\",\r\n \"exceptionData\": null,\r\n \"apiBaseUrl\": \"https://api.esrp.microsoft.com/api/v2/\",\r\n \"handlerWorkflow\": \"Sign\",\r\n \"submissionRequest\": {\r\n \"contextData\": {\r\n \"build_buildnumber\": \"AzureDevOps_M273_20260511.16\",\r\n \"esrpClientVersion\": \"1.2.142+Branch.master.Sha.90404b43284ec55b3e2251d0e272f8932aa583e2\",\r\n \"userIpAddress\": \"10.0.0.128\",\r\n \"userAgent\": \"at-blue8CGPNC\"\r\n },\r\n \"groupId\": null,\r\n \"correlationVector\": \"c3556c28-2050-446d-849b-49f8eae12585\",\r\n \"driEmail\": null,\r\n \"version\": \"1.0.0\"\r\n },\r\n \"policyInfo\": {\r\n \"intent\": \"digestsign\",\r\n \"contentType\": \"Bin\",\r\n \"contentOrigin\": \"3rd Party\",\r\n \"productState\": null,\r\n \"audience\": \"External Broad\",\r\n \"version\": \"1.0.0\"\r\n },\r\n \"osVersion\": \"Microsoft Windows NT 10.0.26100.0\",\r\n \"executingMachineIPAddress\": \"10.0.0.128\"\r\n}\r\n2026-05-30T07:12:54.3833818Z:Executing SignWorkflowType is DigestSignStaticAzure\r\n2026-05-30T07:12:54.3981359Z:Source file \"c:\\Temp\\AzureTemp\\lsdu0yn5.c2d\\manifest.cat\" hashed in 3 ms\r\n2026-05-30T07:12:54.4407960Z:Thread: 1 - Certificate file mutex \"Ojy6ghRtRREoMi0CQw6cUcxGT9npbVdCVPu2aqp6nLE=_CP-464321\" acquired (waited 0 ms).\r\nMSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:54Z] ConfidentialClientApplication 44123454 created\r\n2026-05-30T07:12:54.4533045Z:Gateway Client: Enter AuthenticationProvider, no calls to AAD yet, client id is 0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f, and resource url is https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com, tenant id is 33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:54.4533045Z:Cached token found with name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:54.4533045Z:AAD auth caching is in use with this name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:54.5414453Z:Global cached token is valid from 5/30/2026 7:07:52 AM to 5/31/2026 7:12:52 AM (UTC). Total validity from current time is 86397.4592669 seconds\r\n2026-05-30T07:12:54.5414453Z:Validate and Renew Token if necessary finished: 00:00:00.0871685\r\nMSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:54Z] ConfidentialClientApplication 20852350 created\r\n2026-05-30T07:12:54.5418492Z:Gateway Client: Enter AuthenticationProvider, no calls to AAD yet, client id is 0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f, and resource url is https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com, tenant id is 33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:54.5418492Z:Gateway Client: a new client and client id is created: 19807b54-7c8f-4e9a-9a17-73e878e00b1c\r\n2026-05-30T07:12:54.5418492Z:5/30/2026 7:12:54 AM +00:00:Digest Signing Factory Client type loaded is - [AzureGatewaySubmitter].\r\n2026-05-30T07:12:54.5527498Z:Existing Token Acquisition Time: 00:00:00.0004439\r\n2026-05-30T07:12:54.6201187Z:Gateway Client: the client id that is sending a request is: 19807b54-7c8f-4e9a-9a17-73e878e00b1c\r\n2026-05-30T07:12:55.0620471Z:Gateway Client: response send time from cloud gateway: 2026-05-30T07:12:55.0079134+00:00\r\n2026-05-30T07:12:55.0620471Z:Gateway Client: response receive time from client SendAsync: 2026-05-30T07:12:55.0620471+00:00\r\n2026-05-30T07:12:55.0620471Z:Gateway Client: response duration time between cloud gateway and client SendAsync: 00:00:00.0541337\r\n2026-05-30T07:12:55.0631050Z:Gateway Client: response time after converting from http response to object: 2026-05-30T07:12:55.0631050+00:00\r\n2026-05-30T07:12:55.1552247Z:Client Telemetry: Events published in this batch: 4\r\n2026-05-30T07:12:55.1552247Z:Client Telemetry: Events received so far in this session: 4\r\n2026-05-30T07:12:55.1552247Z:Client Telemetry: Events published so far in this session: 4\r\n2026-05-30T07:12:55.5833157Z:Existing Token Acquisition Time: 00:00:00.0005703\r\n2026-05-30T07:12:55.5867167Z:Gateway Client: the client id that is sending a request is: 19807b54-7c8f-4e9a-9a17-73e878e00b1c\r\n2026-05-30T07:12:56.1075054Z:Gateway Client: response send time from cloud gateway: 2026-05-30T07:12:56.0188537+00:00\r\n2026-05-30T07:12:56.1075054Z:Gateway Client: response receive time from client SendAsync: 2026-05-30T07:12:56.1075054+00:00\r\n2026-05-30T07:12:56.1075054Z:Gateway Client: response duration time between cloud gateway and client SendAsync: 00:00:00.0886517\r\n2026-05-30T07:12:56.1153554Z:Gateway Client: response time after converting from http response to object: 2026-05-30T07:12:56.1153554+00:00\r\n2026-05-30T07:12:56.1199916Z:Reading thumbprint from \"C:\\Windows\\ServiceProfiles\\NetworkService\\AppData\\Local\\EsrpClient_938c323b234749a7b4224d9bb029f850\\Certificates\\CP-464321.p7b\" (9692 bytes)...\r\n2026-05-30T07:12:56.1447761Z:Thread: 1 - Wrote new certificate file to \"C:\\Windows\\ServiceProfiles\\NetworkService\\AppData\\Local\\EsrpClient_938c323b234749a7b4224d9bb029f850\\Certificates\\CP-464321.p7b\".\r\n2026-05-30T07:12:56.1447761Z:Thread: 1 - Certificate file mutex \"Ojy6ghRtRREoMi0CQw6cUcxGT9npbVdCVPu2aqp6nLE=_CP-464321\" released (held for 1704 ms).\r\n2026-05-30T07:12:56.1464729Z:digest sign request expire time is: 5/30/2026 7:13:53 AM\r\n2026-05-30T07:12:59.3454443Z:Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe sign /NPH /fd \"SHA256\" /f \"C:\\Windows\\ServiceProfiles\\NetworkService\\AppData\\Local\\EsrpClient_938c323b234749a7b4224d9bb029f850\\Certificates\\CP-464321.p7b\" /sha1 \"464FA2A9D4A43EA5C3E53CE4562B501CB6EFA47F\" /du \"https://www.1eswiki.com/wiki/ADO_Manifest_Generator\" /d \"Packaging SSSC Codesign - DigestSign\" /tr \"http://aztss.trafficmanager.net/TSS/HttpTspServer\" /td sha256 /dlib \"c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\EsrpClient.Sign.DigestSignLib.dll\" /dmdf \"C:\\Windows\\ServiceProfiles\\NetworkService\\AppData\\Local\\EsrpClient_938c323b234749a7b4224d9bb029f850\\537117FD42D34FD0800EA92D9BED9C8E.json\" \"c:\\Temp\\AzureTemp\\lsdu0yn5.c2d\\manifest.cat\" completed in 3198 ms\r\nExit Code: 0\r\nStdOut:\r\nDone Adding Additional Store\r\n\r\nESRP Digest Signing\r\n\r\n2026-05-30T07:12:57.0843662Z:Digest Signer : SecurityProtocolType used from the metadata info is : Tls12\r\nMSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:57Z] ConfidentialClientApplication 654897 created\r\n2026-05-30T07:12:57.2330813Z:Cached token found with name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:57.2350852Z:AAD auth caching is in use with this name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:12:57.3478577Z:Global cached token is valid from 5/30/2026 7:07:52 AM to 5/31/2026 7:12:52 AM (UTC). Total validity from current time is 86394.6526158 seconds\r\n2026-05-30T07:12:57.3478577Z:Validate and Renew Token if necessary finished: 00:00:00.1121993\r\nMSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:12:57Z] ConfidentialClientApplication 49044892 created\r\n2026-05-30T07:12:57.4198080Z:Existing Token Acquisition Time: 00:00:00.0028461\r\n2026-05-30T07:12:58.2281535Z:Gateway Submission Request Signing Time: 00:00:00.1256469\r\n2026-05-30T07:12:58.2281535Z:Gateway Submission Request Send Time: 00:00:00.6613877\r\n2026-05-30T07:12:58.2281535Z:Gateway Submission Overall Submission Time: 00:00:00.8050043\r\n2026-05-30T07:12:58.2281535Z:Operation ID: 5b55c5a8-780c-4301-a208-a7a756186f5f\r\n2026-05-30T07:12:58.7396907Z:Gateway Status Delay Time: 00:00:00.5110722\r\n2026-05-30T07:12:58.7494518Z:Existing Token Acquisition Time: 00:00:00.0005871\r\n2026-05-30T07:12:59.0527761Z:Gateway Status Request Send Time: 00:00:00.2979035\r\n2026-05-30T07:12:59.0527761Z:Gateway Status Overall Time: 00:00:00.3003616\r\n2026-05-30T07:12:59.0605632Z:Gateway Submission Duration: 1141\r\n2026-05-30T07:12:59.0605632Z:Gateway Submission Attempts: 1\r\n2026-05-30T07:12:59.0605632Z:Gateway GetStatus Duration: 313\r\n2026-05-30T07:12:59.0605632Z:Gateway GetStatus Attempts: 1\r\n2026-05-30T07:12:59.0605632Z:$$5a9f5111cf8f42a0a0edc419862b95dc##_ThrottleCount: 0\r\n2026-05-30T07:12:59.0605632Z:$$65265a48891640eb86e148d3c9a627fa##_ThrottledTimeInSec: 0\r\n2026-05-30T07:12:59.0605632Z:Service Call: 2112.5371 ms\r\nSuccessfully signed: c:\\Temp\\AzureTemp\\lsdu0yn5.c2d\\manifest.cat\r\n\r\n\r\n\r\nStdErr:\r\n\r\n\r\n\r\n\r\n2026-05-30T07:12:59.5435911Z:Warning: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\lsdu0yn5.c2d\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\lsdu0yn5.c2d\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n2026-05-30T07:12:59.5456583Z:Warning: Warning: Operation Error (1) - Operation: signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\lsdu0yn5.c2d\\manifest.cat\" - Retrying in 500 ms...\r\n2026-05-30T07:12:59.6802424Z:Warning: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\lsdu0yn5.c2d\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\lsdu0yn5.c2d\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n2026-05-30T07:12:59.6802424Z:Warning: Warning: Operation Error (2) - Operation: signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\lsdu0yn5.c2d\\manifest.cat\" - Retrying in 500 ms...\r\n2026-05-30T07:12:59.7074053Z:Client Telemetry: Events published in this batch: 3\r\n2026-05-30T07:12:59.7074053Z:Client Telemetry: Events received so far in this session: 7\r\n2026-05-30T07:12:59.7074053Z:Client Telemetry: Events published so far in this session: 7\r\n2026-05-30T07:13:00.2989920Z:Warning: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\lsdu0yn5.c2d\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\lsdu0yn5.c2d\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n2026-05-30T07:13:00.3280533Z:\r\n2026-05-30T07:13:00.3315640Z:Error: System.AggregateException: One or more errors occurred. ---> MS.Ess.EsrpClient.Sign.Exceptions.EsrpDigestSignExecuteProcessFailedException: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\lsdu0yn5.c2d\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\lsdu0yn5.c2d\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass27_0.<ExecuteDigestSignOperation>b__1()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.<>c__DisplayClass1.<ExecuteAction>b__0()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.ExecuteAction[TResult](Func`1 func)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.ExecuteDigestSignOperation(Input digestSignInput, Operation digestSignOperation, SignOperationData fileEntry, SignStatusResponse completionResponse, SignCommandDefinition param, Guid correlationId, String correlationVector, CancellationToken cancellationToken, String requestId, String tmpDestinationLocation)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.DigestSignInternal(Input digestSignInput, SignCommandDefinition param, Guid& correlationId, String correlationVector, CancellationToken cancellationToken, String groupId, SignOperationData fileEntry, Int32& signedFileCount, Int32& certRefreshCount, ConcurrentBag`1 completionResponses, RetryPolicy`1 operationRetryPolicy, String dynamicCertificateFile, String dynamicCertificateThumbprint, String requestId)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass23_3.<DigestSignAsync>b__6(SignOperationData fileEntry)\r\n --- End of inner exception stack trace ---\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<DigestSignAsync>d__23.MoveNext()\r\n---> (Inner Exception #0) MS.Ess.EsrpClient.Sign.Exceptions.EsrpDigestSignExecuteProcessFailedException: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\lsdu0yn5.c2d\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\lsdu0yn5.c2d\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass27_0.<ExecuteDigestSignOperation>b__1()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.<>c__DisplayClass1.<ExecuteAction>b__0()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.ExecuteAction[TResult](Func`1 func)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.ExecuteDigestSignOperation(Input digestSignInput, Operation digestSignOperation, SignOperationData fileEntry, SignStatusResponse completionResponse, SignCommandDefinition param, Guid correlationId, String correlationVector, CancellationToken cancellationToken, String requestId, String tmpDestinationLocation)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.DigestSignInternal(Input digestSignInput, SignCommandDefinition param, Guid& correlationId, String correlationVector, CancellationToken cancellationToken, String groupId, SignOperationData fileEntry, Int32& signedFileCount, Int32& certRefreshCount, ConcurrentBag`1 completionResponses, RetryPolicy`1 operationRetryPolicy, String dynamicCertificateFile, String dynamicCertificateThumbprint, String requestId)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass23_3.<DigestSignAsync>b__6(SignOperationData fileEntry)<---\r\n\r\n2026-05-30T07:13:00.3628828Z:Constructing EsrpClientResponse\r\n2026-05-30T07:13:00.3628828Z:EsrpClientResponse is constructed\r\n2026-05-30T07:13:00.3932747Z:OperationDurationMs: 5988\r\n2026-05-30T07:13:00.3932747Z:DynamicCertGenerationTimeMs: 0\r\n2026-05-30T07:13:00.3932747Z:TimeToGetStorageShradsMs: 0\r\n2026-05-30T07:13:00.3932747Z:TotalScanSubmissionTimeMs: 0\r\n2026-05-30T07:13:00.3932747Z:ThrottleCount: 0\r\n2026-05-30T07:13:00.3932747Z:ThrottledTimeInSec: 0\r\n2026-05-30T07:13:00.3932747Z:\r\n2026-05-30T07:13:00.3932747Z:0/1 files signed in 00:00:05.988 (0 files/s, total number of certs refreshed: 0)\r\n2026-05-30T07:13:00.3960190Z:result json is: {\r\n \"submissionResponses\": [\r\n {\r\n \"fileStatusDetail\": [\r\n {\r\n \"sourceHash\": \"riKD+Y2I6Ywewx9VdXctwzcqYvLLMzofeWhIRuecmTY=\",\r\n \"hashType\": \"sha256\",\r\n \"destinationHash\": null,\r\n \"certificateThumbprint\": null,\r\n \"destinationLocation\": \"c:\\\\Temp\\\\AzureTemp\\\\lsdu0yn5.c2d\\\\manifest.cat\",\r\n \"destinationFileSizeInBytes\": 0,\r\n \"sourceLocation\": \"c:\\\\Temp\\\\AzureTemp\\\\lsdu0yn5.c2d\\\\manifest.cat\"\r\n }\r\n ],\r\n \"operationId\": \"5b55c5a8-780c-4301-a208-a7a756186f5f\",\r\n \"customerCorrelationId\": \"c3556c28-2050-446d-849b-49f8eae12585\",\r\n \"statusCode\": \"failCanRetry\",\r\n \"errorInfo\": {\r\n \"code\": \"3138\",\r\n \"details\": {\r\n \"operation\": \"c:\\\\AT\\\\sitesroot\\\\0\\\\bin\\\\Plugins\\\\ESRPClient\\\\Win10.x86\\\\signtool.exe verify /pa /tw \\\"c:\\\\Temp\\\\AzureTemp\\\\lsdu0yn5.c2d\\\\manifest.cat\\\"\",\r\n \"exitCode\": \"1\",\r\n \"stdOut\": \"File: c:\\\\Temp\\\\AzureTemp\\\\lsdu0yn5.c2d\\\\manifest.cat\\r\\nIndex Algorithm Timestamp \\r\\n========================================\\r\\n\\r\\nNumber of errors: 1\\r\\n\\r\\n\\r\\n\",\r\n \"stdErr\": \"SignTool Error: A certificate chain processed, but terminated in a root\\r\\n\\tcertificate which is not trusted by the trust provider.\\r\\n\\r\\n\"\r\n },\r\n \"innerError\": null\r\n }\r\n }\r\n ],\r\n \"esrpClientSessionGuid\": \"938c323b-2347-49a7-b422-4d9bb029f850\",\r\n \"version\": \"1.0.0\"\r\n}\r\n2026-05-30T07:13:00.4439269Z:Warning: \r\n\r\n2026-05-30T07:13:00.4445280Z:esrp-client-finalTime: 10444.0565\r\n2026-05-30T07:13:00.4445280Z:Final return code is: 1\r\n2026-05-30T07:13:00.4445280Z:ClientTelemetryDispose started, this is not affecting Exe reliability, only lost telemetry if error happens ***********************\r\n2026-05-30T07:13:00.4488513Z:Client Telemetry: Flushing telemetry buffer with timeout 0 ms\r\n2026-05-30T07:13:00.4506277Z:Client Telemetry: Flushing telemetry buffer completed within timeout (true/false): False\r\n2026-05-30T07:13:00.4506277Z:Client Telemetry: Disposing telemetry buffer and event hub client\r\n2026-05-30T07:13:00.4586607Z:Client Telemetry: Events published in this batch: 4\r\n2026-05-30T07:13:00.4586607Z:Client Telemetry: Events received so far in this session: 11\r\n2026-05-30T07:13:00.4586607Z:Client Telemetry: Events published so far in this session: 11\r\n2026-05-30T07:13:00.5125766Z:event lost number: 0\r\n2026-05-30T07:13:00.5125766Z:total event processed number: 11\r\n2026-05-30T07:13:00.5125766Z:total event received number: 11\r\n2026-05-30T07:13:00.5125766Z:##EsrpClientTelemetry.TotalEventsProcessed##: 11\r\n2026-05-30T07:13:00.5125766Z:##EsrpClientTelemetry.TotalEventsReceived##: 11\r\n2026-05-30T07:13:00.5125766Z:##EsrpClientTelemetry.DisposeTimeMilliseconds##: 68\r\n2026-05-30T07:13:00.5125766Z:ClientTelemetryDispose ended ***********************\r\n","StdErr":"2026-05-30T07:13:00.3315640Z:Error: System.AggregateException: One or more errors occurred. ---> MS.Ess.EsrpClient.Sign.Exceptions.EsrpDigestSignExecuteProcessFailedException: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\lsdu0yn5.c2d\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\lsdu0yn5.c2d\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass27_0.<ExecuteDigestSignOperation>b__1()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.<>c__DisplayClass1.<ExecuteAction>b__0()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.ExecuteAction[TResult](Func`1 func)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.ExecuteDigestSignOperation(Input digestSignInput, Operation digestSignOperation, SignOperationData fileEntry, SignStatusResponse completionResponse, SignCommandDefinition param, Guid correlationId, String correlationVector, CancellationToken cancellationToken, String requestId, String tmpDestinationLocation)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.DigestSignInternal(Input digestSignInput, SignCommandDefinition param, Guid& correlationId, String correlationVector, CancellationToken cancellationToken, String groupId, SignOperationData fileEntry, Int32& signedFileCount, Int32& certRefreshCount, ConcurrentBag`1 completionResponses, RetryPolicy`1 operationRetryPolicy, String dynamicCertificateFile, String dynamicCertificateThumbprint, String requestId)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass23_3.<DigestSignAsync>b__6(SignOperationData fileEntry)\r\n --- End of inner exception stack trace ---\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<DigestSignAsync>d__23.MoveNext()\r\n---> (Inner Exception #0) MS.Ess.EsrpClient.Sign.Exceptions.EsrpDigestSignExecuteProcessFailedException: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\lsdu0yn5.c2d\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\lsdu0yn5.c2d\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass27_0.<ExecuteDigestSignOperation>b__1()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.<>c__DisplayClass1.<ExecuteAction>b__0()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.ExecuteAction[TResult](Func`1 func)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.ExecuteDigestSignOperation(Input digestSignInput, Operation digestSignOperation, SignOperationData fileEntry, SignStatusResponse completionResponse, SignCommandDefinition param, Guid correlationId, String correlationVector, CancellationToken cancellationToken, String requestId, String tmpDestinationLocation)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.DigestSignInternal(Input digestSignInput, SignCommandDefinition param, Guid& correlationId, String correlationVector, CancellationToken cancellationToken, String groupId, SignOperationData fileEntry, Int32& signedFileCount, Int32& certRefreshCount, ConcurrentBag`1 completionResponses, RetryPolicy`1 operationRetryPolicy, String dynamicCertificateFile, String dynamicCertificateThumbprint, String requestId)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass23_3.<DigestSignAsync>b__6(SignOperationData fileEntry)<---\r\n\r\n","ExitCode":1,"RunningTime":"00:00:11.3401245"} |
| {"StdOut":"******************************************************************************\r\nMachine Information\r\nMachine Name: at-blueAUTV7F\r\nMachine Ip: 10.0.0.67\r\nOperating System: Microsoft Windows NT 10.0.26100.0\r\nUser Name: at-blueAUTV7F$\r\nProcessor Count: 64\r\nProcess Name: EsrpClient\r\nProcess Id: 15808\r\nCaller Program: EsrpClient.exe\r\nIdentity: NT AUTHORITY\\NETWORK SERVICE\r\nProcess Version: 1.2.142+Branch.master.Sha.90404b43284ec55b3e2251d0e272f8932aa583e2\r\nProcess Bitness: 64 bit\r\n******************************************************************************\r\nCommandline received: Sign | -a | c:\\Temp\\AzureTemp\\TFSTemp\\vctmp10248_614322.json | -p | c:\\Temp\\AzureTemp\\TFSTemp\\vctmp10248_102784.json | -c | c:\\Temp\\AzureTemp\\TFSTemp\\vctmp10248_562490.json | -i | c:\\Temp\\AzureTemp\\TFSTemp\\vctmp1148_758893.json | -o | c:\\Temp\\AzureTemp\\TFSTemp\\v40D06A.tmp | -l | Verbose ESRP session Id is: 03875b26-f409-451f-a3ec-e4ab39621ce7\r\n2026-05-30T07:22:42.2981849Z:Command you are trying to use is: Sign\r\n2026-05-30T07:22:42.3031305Z:Correlation Vector for this run is: af9105ef-bb2c-4a78-83db-b4981648bb23\r\n2026-05-30T07:22:42.3031305Z:Groupid for this run is empty\r\n2026-05-30T07:22:42.4211318Z:request signing cert being used is this thumbprint: 42661F29AF78973B30992C76D622575F555E3D84 in store LocalMachine\\My\r\n2026-05-30T07:22:42.4217447Z:Both certificates validation passed.\r\n2026-05-30T07:22:42.4231398Z:The auth cert we choose to use, subject name is: CN=0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f.microsoft.com, thumbprint is: 943507930F1390CF64FC5E9DB45F03091556EEBE\r\n2026-05-30T07:22:42.4819548Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:42Z] ConfidentialClientApplication 37368736 created\r\n2026-05-30T07:22:42.4824358Z:Gateway Client: Enter AuthenticationProvider, no calls to AAD yet, client id is 0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f, and resource url is https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com, tenant id is 33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:22:42.4881072Z:There is no memory mapped file accociated with this name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:22:42.4904720Z:AAD auth caching is in use with this name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:22:42.4999917Z:Validate and Renew Token if necessary finished: 00:00:00.0081359\r\n2026-05-30T07:22:42.6128853Z:Session request is: {\r\n \"expiresAfter\": \"3.00:00:00\",\r\n \"partitionCount\": 0,\r\n \"isProvisionStorage\": true,\r\n \"isLegacyCopsModel\": false,\r\n \"commandName\": \"Sign\",\r\n \"intent\": \"digestsign\",\r\n \"contentType\": \"Bin\",\r\n \"contentOrigin\": \"3rd Party\",\r\n \"productState\": null,\r\n \"audience\": \"External Broad\"\r\n}\r\n2026-05-30T07:22:42.6558162Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:42Z] ConfidentialClientApplication 29578451 created\r\n2026-05-30T07:22:42.6558162Z:Gateway Client: Enter AuthenticationProvider, no calls to AAD yet, client id is 0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f, and resource url is https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com, tenant id is 33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:22:42.6633537Z:Gateway Client: a new client and client id is created: 2ebdf67d-84ee-4e0a-8c2f-30d4a2ed2480\r\n2026-05-30T07:22:42.7038018Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:42Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] MSAL MSAL.Desktop with assembly version '4.70.0.0'. CorrelationId(c0c2a7a0-7573-4504-8e3f-30b0f4ad6798)\r\n2026-05-30T07:22:42.7173390Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:42Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] === AcquireTokenForClientParameters ===\r\nSendX5C: True\r\nForceRefresh: False\r\nAccessTokenHashToRefresh: False\r\n\r\n2026-05-30T07:22:42.7313547Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:42Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] \r\n=== Request Data ===\r\nAuthority Provided? - True\r\nScopes - https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com/.default\r\nExtra Query Params Keys (space separated) - \r\nApiId - AcquireTokenForClient\r\nIsConfidentialClient - True\r\nSendX5C - True\r\nLoginHint ? False\r\nIsBrokerConfigured - False\r\nHomeAccountId - False\r\nCorrelationId - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798\r\nUserAssertion set: False\r\nLongRunningOboCacheKey set: False\r\nRegion configured: \r\n\r\n2026-05-30T07:22:42.7324401Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:42Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] === Token Acquisition (ClientCredentialRequest) started:\r\n\t Scopes: https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com/.default\r\n\tAuthority Host: login.microsoftonline.com\r\n2026-05-30T07:22:42.7487394Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:42Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] [Internal cache] Total number of cache partitions found while getting access tokens: 0\r\n2026-05-30T07:22:42.7487394Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:42Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] [FindAccessTokenAsync] Discovered 0 access tokens in cache using partition key: 0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d_AppTokenCache\r\n2026-05-30T07:22:42.7503754Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:42Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] [FindAccessTokenAsync] No access tokens found in the cache. Skipping filtering. \r\n2026-05-30T07:22:42.7608209Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:42Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] [Instance Discovery] Instance discovery is enabled and will be performed\r\n2026-05-30T07:22:42.7647649Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:42Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] [Region discovery] WithAzureRegion not configured. \r\n2026-05-30T07:22:42.7647649Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:42Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] [Region discovery] Not using a regional authority. \r\n2026-05-30T07:22:42.7687920Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:42Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] [Instance Discovery] Tried to use network cache provider for login.microsoftonline.com. Success? False. \r\n2026-05-30T07:22:42.7808883Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:42Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] Fetching instance discovery from the network from host login.microsoftonline.com. \r\n2026-05-30T07:22:42.7984480Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:42Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] Starting [Oauth2Client] Sending GET request \r\n2026-05-30T07:22:42.8024614Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:42Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] Starting [HttpManager] ExecuteAsync\r\n2026-05-30T07:22:42.8098333Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:42Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] [HttpManager] Sending request. Method: GET. Host: https://login.microsoftonline.com. Binding Certificate: False \r\n2026-05-30T07:22:43.4931135Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] [HttpManager] Received response. Status code: OK. \r\n2026-05-30T07:22:43.5060992Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] Finished [HttpManager] ExecuteAsync in 703 ms\r\n2026-05-30T07:22:43.5060992Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] Finished [Oauth2Client] Sending GET request in 707 ms\r\n2026-05-30T07:22:43.5167422Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] Starting [OAuth2Client] Deserializing response\r\n2026-05-30T07:22:43.6417395Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] Finished [OAuth2Client] Deserializing response in 125 ms\r\n2026-05-30T07:22:43.6435558Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] [Instance Discovery] Tried to use network cache provider for login.microsoftonline.com. Success? True. \r\n2026-05-30T07:22:43.6435558Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] [Instance Discovery] After hitting the discovery endpoint, the network provider found an entry for login.microsoftonline.com ? True. \r\n2026-05-30T07:22:43.6479930Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] Authority validation enabled? True. \r\n2026-05-30T07:22:43.6479930Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] Authority validation - is known env? True. \r\n2026-05-30T07:22:43.6667272Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] Starting TokenClient:SendTokenRequestAsync\r\n2026-05-30T07:22:43.6707493Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] [TokenClient] Before adding the client assertion / secret\r\n2026-05-30T07:22:43.6711685Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] Building assertion from certificate with clientId: 0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f at endpoint: https://login.microsoftonline.com/33e01921-4d64-4f8c-a055-5bdaffd5e33d/oauth2/v2.0/token\r\n2026-05-30T07:22:43.6711685Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] Proceeding with JWT token creation and adding client assertion.\r\n2026-05-30T07:22:43.7063168Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] [TokenClient] After adding the client assertion / secret\r\n2026-05-30T07:22:43.7162311Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] [Token Client] Fetching MsalTokenResponse .... \r\n2026-05-30T07:22:43.7162311Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] Starting [Oauth2Client] Sending POST request \r\n2026-05-30T07:22:43.7221214Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] Starting [HttpManager] ExecuteAsync\r\n2026-05-30T07:22:43.7221214Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] [HttpManager] Sending request. Method: POST. Host: https://login.microsoftonline.com. Binding Certificate: False \r\n2026-05-30T07:22:43.9580051Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] [HttpManager] Received response. Status code: OK. \r\n2026-05-30T07:22:43.9580051Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] Finished [HttpManager] ExecuteAsync in 235 ms\r\n2026-05-30T07:22:43.9580051Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] Finished [Oauth2Client] Sending POST request in 242 ms\r\n2026-05-30T07:22:43.9585169Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] Starting [OAuth2Client] Deserializing response\r\n2026-05-30T07:22:43.9724932Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] Finished [OAuth2Client] Deserializing response in 15 ms\r\n2026-05-30T07:22:43.9729380Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] ScopeSet was missing from the token response, so using developer provided scopes in the result. \r\n2026-05-30T07:22:43.9729380Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] Finished TokenClient:SendTokenRequestAsync in 306 ms\r\n2026-05-30T07:22:43.9746513Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] Checking client info returned from the server..\r\n2026-05-30T07:22:43.9746513Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] Saving token response to cache..\r\n2026-05-30T07:22:43.9843528Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] \r\n[MsalTokenResponse]\r\nError: \r\nErrorDescription: \r\nScopes: https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com/.default\r\nExpiresIn: 86399\r\nRefreshIn: 43199\r\nAccessToken returned: True\r\nAccessToken Type: Bearer\r\nRefreshToken returned: False\r\nIdToken returned: False\r\nClientInfo returned: False\r\nFamilyId: \r\nWamAccountId exists: False\r\n2026-05-30T07:22:43.9843528Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] [SaveTokenResponseAsync] ID Token not present in response. \r\n2026-05-30T07:22:43.9868232Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] Cannot determine home account ID - or id token or no client info and no subject \r\n2026-05-30T07:22:43.9928453Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] [SaveTokenResponseAsync] Entering token cache semaphore. Count Real semaphore: True. Count: 1.\r\n2026-05-30T07:22:43.9928453Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] [SaveTokenResponseAsync] Entered token cache semaphore. \r\n2026-05-30T07:22:43.9928453Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] [SaveTokenResponseAsync] Saving AT in cache and removing overlapping ATs...\r\n2026-05-30T07:22:43.9956884Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] Looking for scopes for the authority in the cache which intersect with https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com/.default\r\n2026-05-30T07:22:43.9956884Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z] [Internal cache] Total number of cache partitions found while getting access tokens: 0\r\n2026-05-30T07:22:43.9956884Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] Intersecting scope entries count - 0\r\n2026-05-30T07:22:43.9975790Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] Not saving to ADAL legacy cache. \r\n2026-05-30T07:22:43.9975790Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:43Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] [SaveTokenResponseAsync] Released token cache semaphore. \r\n2026-05-30T07:22:44.0015916Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:44Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] \r\n\t=== Token Acquisition finished successfully:\r\n2026-05-30T07:22:44.0026411Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:44Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] AT expiration time: 5/31/2026 7:22:42 AM +00:00, scopes: https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com/.default. source: IdentityProvider\r\n2026-05-30T07:22:44.0026411Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:44Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] Fetched access token from host login.microsoftonline.com. \r\n2026-05-30T07:22:44.0041136Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:44Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] \r\n[LogMetricsFromAuthResult] Cache Refresh Reason: NoCachedAccessToken\r\n[LogMetricsFromAuthResult] DurationInCacheInMs: 0\r\n[LogMetricsFromAuthResult] DurationTotalInMs: 1278\r\n[LogMetricsFromAuthResult] DurationInHttpInMs: 917\r\n2026-05-30T07:22:44.0041136Z:MSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:44Z - c0c2a7a0-7573-4504-8e3f-30b0f4ad6798] TokenEndpoint: ****\r\n2026-05-30T07:22:44.0202809Z:Use AAD token. CERT\r\n2026-05-30T07:22:44.0311889Z:New Token Acquisition Time: 00:00:01.3577902\r\n2026-05-30T07:22:44.0594763Z:Gateway Client: the client id that is sending a request is: 2ebdf67d-84ee-4e0a-8c2f-30d4a2ed2480\r\n2026-05-30T07:22:44.6175089Z:Gateway Client: response send time from cloud gateway: 2026-05-30T07:22:44.5654447+00:00\r\n2026-05-30T07:22:44.6175089Z:Gateway Client: response receive time from client SendAsync: 2026-05-30T07:22:44.6175089+00:00\r\n2026-05-30T07:22:44.6175089Z:Gateway Client: response duration time between cloud gateway and client SendAsync: 00:00:00.0520642\r\n2026-05-30T07:22:44.7194213Z:Session request requestid: 1a973507-d74c-4b69-9cd8-54d31c1177d3, and submission status is: Pass\r\n2026-05-30T07:22:44.7761264Z:Provision storage complete. Total shards: 100\r\n2026-05-30T07:22:44.7789031Z:Loading DigestSignErrorMappingCache mapping info\r\n2026-05-30T07:22:44.7876356Z:DigestSignErrorMappingCache from server, # of DigestSignOperationErrorPatterns object we get is :: \r\n2026-05-30T07:22:44.8328824Z:Consolidate DigestSignErrorMappingCache, # of DigestSignOperationErrorPatterns object we get is :: 34\r\n2026-05-30T07:22:44.8328824Z:Successfully retrieved policy: policy is {\"policy\":{\"id\":\"0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f-0\",\"workflowExecutionType\":3}}\r\n2026-05-30T07:22:44.8445242Z:Successfully get telemetry connection string: Endpo......\r\n2026-05-30T07:22:44.8465549Z:Warning: \r\n2026-05-30T07:22:44.8504105Z:IAuthInfo constructed: {\r\n \"authenticationType\": \"AAD_CERT\",\r\n \"clientId\": \"0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f\",\r\n \"tenantId\": \"33e01921-4d64-4f8c-a055-5bdaffd5e33d\",\r\n \"aadAuthorityBaseUri\": \"https://login.microsoftonline.com/\",\r\n \"authCert\": {\r\n \"storeLocation\": \"LocalMachine\",\r\n \"storeName\": \"My\",\r\n \"subjectName\": \"CN=0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f.microsoft.com\",\r\n \"sendX5c\": true,\r\n \"withAzureRegion\": false,\r\n \"getCertFromKeyVault\": false,\r\n \"keyVaultName\": null,\r\n \"keyVaultCertName\": null\r\n },\r\n \"requestSigningCert\": {\r\n \"storeLocation\": \"LocalMachine\",\r\n \"storeName\": \"My\",\r\n \"subjectName\": \"CN=0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f\",\r\n \"sendX5c\": false,\r\n \"withAzureRegion\": false,\r\n \"getCertFromKeyVault\": false,\r\n \"keyVaultName\": null,\r\n \"keyVaultCertName\": null\r\n },\r\n \"oAuthToken\": null,\r\n \"version\": \"1.0.0\",\r\n \"esrpClientId\": \"0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f\",\r\n \"federatedTokenData\": null,\r\n \"federatedTokenPath\": null\r\n}\r\n2026-05-30T07:22:44.8504105Z:IPolicyInfo constructed: {\r\n \"intent\": \"digestsign\",\r\n \"contentType\": \"Bin\",\r\n \"contentOrigin\": \"3rd Party\",\r\n \"productState\": null,\r\n \"audience\": \"External Broad\",\r\n \"version\": \"1.0.0\"\r\n}\r\n2026-05-30T07:22:44.8551706Z:IConfigInfo constructed: {\r\n \"esrpApiBaseUri\": \"https://api.esrp.microsoft.com/api/v2/\",\r\n \"esrpSessionTimeoutInSec\": 60,\r\n \"minThreadPoolThreads\": 256,\r\n \"maxDegreeOfParallelism\": 256,\r\n \"exponentialFirstFastRetry\": true,\r\n \"exponentialRetryCount\": 2,\r\n \"exponentialRetryDeltaBackOff\": \"00:00:05\",\r\n \"exponentialRetryMaxBackOff\": \"00:01:00\",\r\n \"exponentialRetryMinBackOff\": \"00:00:03\",\r\n \"appDataFolder\": \"C:\\\\Windows\\\\ServiceProfiles\\\\NetworkService\\\\AppData\\\\Local\",\r\n \"certificateCacheFolder\": null,\r\n \"version\": \"1.0.0\",\r\n \"exitOnFlaggedFile\": false,\r\n \"flaggedFileClientWaitTimeout\": \"1.00:00:00\",\r\n \"servicePointManagerDefaultConnectionLimit\": 256,\r\n \"isOnPremGateway\": false,\r\n \"diagnosticListeners\": null,\r\n \"securityProtocolType\": \"Tls12\",\r\n \"parallelOperationsInFileUploadDownload\": 300,\r\n \"maxTelemetryBuffer\": 200000,\r\n \"telemetryTimeoutInSec\": 0,\r\n \"resourceUri\": \"https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com\",\r\n \"cacheRootFolder\": null,\r\n \"cachedFileTTLInMin\": 7200\r\n}\r\n2026-05-30T07:22:45.1094035Z:Client Telemetry: Xpert agent is not running on the machine. Using events hub for processing client telemetry.\r\n2026-05-30T07:22:45.3121091Z:Key: TotalSignOperationDataCount, Value: 1\r\n2026-05-30T07:22:45.3294350Z:Start Time:5/30/2026 7:22:45 AM, Starting the sign workflow...\r\n2026-05-30T07:22:45.3431911Z:some input info: {\r\n \"contextData\": {\r\n \"build_buildnumber\": \"AzureDevOps_M273_20260511.16\",\r\n \"esrpClientVersion\": \"1.2.142+Branch.master.Sha.90404b43284ec55b3e2251d0e272f8932aa583e2\",\r\n \"userIpAddress\": \"10.0.0.67\",\r\n \"userAgent\": \"at-blueAUTV7F\"\r\n },\r\n \"sourceDirectory\": null,\r\n \"sourceLocation\": \"c:\\\\Temp\\\\AzureTemp\\\\p1kqilux.doz\\\\manifest.cat\",\r\n \"destinationDirectory\": null,\r\n \"destinationLocation\": \"c:\\\\Temp\\\\AzureTemp\\\\p1kqilux.doz\\\\manifest.cat\",\r\n \"sizeInBytes\": 0,\r\n \"name\": \"manifest.cat\",\r\n \"isSuccess\": null,\r\n \"operationStartedAt\": \"0001-01-01T00:00:00+00:00\",\r\n \"operationEndedAt\": \"0001-01-01T00:00:00+00:00\",\r\n \"operationDurationMS\": 0,\r\n \"processName\": \"EsrpClient\",\r\n \"processId\": 15808,\r\n \"processVersion\": \"1.2.142+Branch.master.Sha.90404b43284ec55b3e2251d0e272f8932aa583e2\",\r\n \"customerCorrelationId\": null,\r\n \"esrpClientSessionGuid\": \"03875b26-f409-451f-a3ec-e4ab39621ce7\",\r\n \"callerProgram\": \"EsrpClient\",\r\n \"indentity\": \"NT AUTHORITY\\\\NETWORK SERVICE\",\r\n \"clientId\": \"0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f\",\r\n \"exceptionData\": null,\r\n \"apiBaseUrl\": \"https://api.esrp.microsoft.com/api/v2/\",\r\n \"handlerWorkflow\": \"Sign\",\r\n \"submissionRequest\": {\r\n \"contextData\": {\r\n \"build_buildnumber\": \"AzureDevOps_M273_20260511.16\",\r\n \"esrpClientVersion\": \"1.2.142+Branch.master.Sha.90404b43284ec55b3e2251d0e272f8932aa583e2\",\r\n \"userIpAddress\": \"10.0.0.67\",\r\n \"userAgent\": \"at-blueAUTV7F\"\r\n },\r\n \"groupId\": null,\r\n \"correlationVector\": \"af9105ef-bb2c-4a78-83db-b4981648bb23\",\r\n \"driEmail\": null,\r\n \"version\": \"1.0.0\"\r\n },\r\n \"policyInfo\": {\r\n \"intent\": \"digestsign\",\r\n \"contentType\": \"Bin\",\r\n \"contentOrigin\": \"3rd Party\",\r\n \"productState\": null,\r\n \"audience\": \"External Broad\",\r\n \"version\": \"1.0.0\"\r\n },\r\n \"osVersion\": \"Microsoft Windows NT 10.0.26100.0\",\r\n \"executingMachineIPAddress\": \"10.0.0.67\"\r\n}\r\n2026-05-30T07:22:45.3882163Z:Executing SignWorkflowType is DigestSignStaticAzure\r\n2026-05-30T07:22:45.4045651Z:Source file \"c:\\Temp\\AzureTemp\\p1kqilux.doz\\manifest.cat\" hashed in 3 ms\r\n2026-05-30T07:22:45.4488353Z:Thread: 1 - Certificate file mutex \"4qpgAxGr37i1+v/g6O3jGppwl7H9u76nsjg0Fj/Xeqw=_CP-464321\" acquired (waited 0 ms).\r\nMSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:45Z] ConfidentialClientApplication 44123454 created\r\n2026-05-30T07:22:45.4618764Z:Gateway Client: Enter AuthenticationProvider, no calls to AAD yet, client id is 0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f, and resource url is https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com, tenant id is 33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:22:45.4625991Z:Cached token found with name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:22:45.4631825Z:AAD auth caching is in use with this name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:22:45.5519908Z:Global cached token is valid from 5/30/2026 7:17:43 AM to 5/31/2026 7:22:43 AM (UTC). Total validity from current time is 86397.4488437 seconds\r\n2026-05-30T07:22:45.5519908Z:Validate and Renew Token if necessary finished: 00:00:00.0890067\r\nMSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:45Z] ConfidentialClientApplication 20852350 created\r\n2026-05-30T07:22:45.5519908Z:Gateway Client: Enter AuthenticationProvider, no calls to AAD yet, client id is 0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f, and resource url is https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com, tenant id is 33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:22:45.5519908Z:Gateway Client: a new client and client id is created: d0d431e3-fc87-4dbe-ba2e-04a604990688\r\n2026-05-30T07:22:45.5519908Z:5/30/2026 7:22:45 AM +00:00:Digest Signing Factory Client type loaded is - [AzureGatewaySubmitter].\r\n2026-05-30T07:22:45.5627127Z:Existing Token Acquisition Time: 00:00:00.0004200\r\n2026-05-30T07:22:45.6381887Z:Gateway Client: the client id that is sending a request is: d0d431e3-fc87-4dbe-ba2e-04a604990688\r\n2026-05-30T07:22:45.9748855Z:Gateway Client: response send time from cloud gateway: 2026-05-30T07:22:45.9438314+00:00\r\n2026-05-30T07:22:45.9748855Z:Gateway Client: response receive time from client SendAsync: 2026-05-30T07:22:45.9748855+00:00\r\n2026-05-30T07:22:45.9748855Z:Gateway Client: response duration time between cloud gateway and client SendAsync: 00:00:00.0310541\r\n2026-05-30T07:22:45.9765808Z:Gateway Client: response time after converting from http response to object: 2026-05-30T07:22:45.9765808+00:00\r\n2026-05-30T07:22:46.1372869Z:Client Telemetry: Events published in this batch: 4\r\n2026-05-30T07:22:46.1372869Z:Client Telemetry: Events received so far in this session: 4\r\n2026-05-30T07:22:46.1372869Z:Client Telemetry: Events published so far in this session: 4\r\n2026-05-30T07:22:46.4922880Z:Existing Token Acquisition Time: 00:00:00.0007202\r\n2026-05-30T07:22:46.4961344Z:Gateway Client: the client id that is sending a request is: d0d431e3-fc87-4dbe-ba2e-04a604990688\r\n2026-05-30T07:22:46.6781382Z:Gateway Client: response send time from cloud gateway: 2026-05-30T07:22:46.6569149+00:00\r\n2026-05-30T07:22:46.6796336Z:Gateway Client: response receive time from client SendAsync: 2026-05-30T07:22:46.6796336+00:00\r\n2026-05-30T07:22:46.6796336Z:Gateway Client: response duration time between cloud gateway and client SendAsync: 00:00:00.0227187\r\n2026-05-30T07:22:46.6868401Z:Gateway Client: response time after converting from http response to object: 2026-05-30T07:22:46.6868401+00:00\r\n2026-05-30T07:22:46.6918548Z:Reading thumbprint from \"C:\\Windows\\ServiceProfiles\\NetworkService\\AppData\\Local\\EsrpClient_03875b26f409451fa3ece4ab39621ce7\\Certificates\\CP-464321.p7b\" (9692 bytes)...\r\n2026-05-30T07:22:46.7224872Z:Thread: 1 - Wrote new certificate file to \"C:\\Windows\\ServiceProfiles\\NetworkService\\AppData\\Local\\EsrpClient_03875b26f409451fa3ece4ab39621ce7\\Certificates\\CP-464321.p7b\".\r\n2026-05-30T07:22:46.7224872Z:Thread: 1 - Certificate file mutex \"4qpgAxGr37i1+v/g6O3jGppwl7H9u76nsjg0Fj/Xeqw=_CP-464321\" released (held for 1273 ms).\r\n2026-05-30T07:22:46.7224872Z:digest sign request expire time is: 5/30/2026 7:23:44 AM\r\n2026-05-30T07:22:49.6854417Z:Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe sign /NPH /fd \"SHA256\" /f \"C:\\Windows\\ServiceProfiles\\NetworkService\\AppData\\Local\\EsrpClient_03875b26f409451fa3ece4ab39621ce7\\Certificates\\CP-464321.p7b\" /sha1 \"464FA2A9D4A43EA5C3E53CE4562B501CB6EFA47F\" /du \"https://www.1eswiki.com/wiki/ADO_Manifest_Generator\" /d \"Packaging SSSC Codesign - DigestSign\" /tr \"http://aztss.trafficmanager.net/TSS/HttpTspServer\" /td sha256 /dlib \"c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\EsrpClient.Sign.DigestSignLib.dll\" /dmdf \"C:\\Windows\\ServiceProfiles\\NetworkService\\AppData\\Local\\EsrpClient_03875b26f409451fa3ece4ab39621ce7\\3FB35F08121F44308B540700A4C5582E.json\" \"c:\\Temp\\AzureTemp\\p1kqilux.doz\\manifest.cat\" completed in 2961 ms\r\nExit Code: 0\r\nStdOut:\r\nDone Adding Additional Store\r\n\r\nESRP Digest Signing\r\n\r\n2026-05-30T07:22:47.6511621Z:Digest Signer : SecurityProtocolType used from the metadata info is : Tls12\r\nMSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:47Z] ConfidentialClientApplication 654897 created\r\n2026-05-30T07:22:47.7977733Z:Cached token found with name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:22:47.7996166Z:AAD auth caching is in use with this name: api.esrp.microsoft.com_0d3b5b1a-8684-4f3a-b294-66c3b9aa8c8f_33e01921-4d64-4f8c-a055-5bdaffd5e33d\r\n2026-05-30T07:22:47.9135148Z:Global cached token is valid from 5/30/2026 7:17:43 AM to 5/31/2026 7:22:43 AM (UTC). Total validity from current time is 86395.0864852 seconds\r\n2026-05-30T07:22:47.9135148Z:Validate and Renew Token if necessary finished: 00:00:00.1138838\r\nMSAL logging: False MSAL 4.70.0.0 MSAL.Desktop 4.8 or later Windows Server 2025 Datacenter Azure Edition [2026-05-30 07:22:47Z] ConfidentialClientApplication 49044892 created\r\n2026-05-30T07:22:47.9870940Z:Existing Token Acquisition Time: 00:00:00.0031008\r\n2026-05-30T07:22:48.8202538Z:Gateway Submission Request Signing Time: 00:00:00.1120187\r\n2026-05-30T07:22:48.8202538Z:Gateway Submission Request Send Time: 00:00:00.6998978\r\n2026-05-30T07:22:48.8202538Z:Gateway Submission Overall Submission Time: 00:00:00.8298423\r\n2026-05-30T07:22:48.8202538Z:Operation ID: e95f5f39-e72a-4ffd-91c9-8d6f94186620\r\n2026-05-30T07:22:49.3212965Z:Gateway Status Delay Time: 00:00:00.5007234\r\n2026-05-30T07:22:49.3320912Z:Existing Token Acquisition Time: 00:00:00.0008199\r\n2026-05-30T07:22:49.3955195Z:Gateway Status Request Send Time: 00:00:00.0588709\r\n2026-05-30T07:22:49.3955195Z:Gateway Status Overall Time: 00:00:00.0612988\r\n2026-05-30T07:22:49.4032383Z:Gateway Submission Duration: 1166\r\n2026-05-30T07:22:49.4032383Z:Gateway Submission Attempts: 1\r\n2026-05-30T07:22:49.4032383Z:Gateway GetStatus Duration: 74\r\n2026-05-30T07:22:49.4032383Z:Gateway GetStatus Attempts: 1\r\n2026-05-30T07:22:49.4032383Z:$$5a9f5111cf8f42a0a0edc419862b95dc##_ThrottleCount: 0\r\n2026-05-30T07:22:49.4032383Z:$$65265a48891640eb86e148d3c9a627fa##_ThrottledTimeInSec: 0\r\n2026-05-30T07:22:49.4032383Z:Service Call: 1885.2923 ms\r\nSuccessfully signed: c:\\Temp\\AzureTemp\\p1kqilux.doz\\manifest.cat\r\n\r\n\r\n\r\nStdErr:\r\n\r\n\r\n\r\n\r\n2026-05-30T07:22:49.8788287Z:Warning: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\p1kqilux.doz\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\p1kqilux.doz\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n2026-05-30T07:22:49.8808374Z:Warning: Warning: Operation Error (1) - Operation: signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\p1kqilux.doz\\manifest.cat\" - Retrying in 500 ms...\r\n2026-05-30T07:22:50.0362522Z:Warning: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\p1kqilux.doz\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\p1kqilux.doz\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n2026-05-30T07:22:50.0362522Z:Warning: Warning: Operation Error (2) - Operation: signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\p1kqilux.doz\\manifest.cat\" - Retrying in 500 ms...\r\n2026-05-30T07:22:50.1967216Z:Client Telemetry: Events published in this batch: 3\r\n2026-05-30T07:22:50.1967216Z:Client Telemetry: Events received so far in this session: 7\r\n2026-05-30T07:22:50.1967216Z:Client Telemetry: Events published so far in this session: 7\r\n2026-05-30T07:22:50.6605247Z:Warning: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\p1kqilux.doz\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\p1kqilux.doz\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n2026-05-30T07:22:50.6972708Z:\r\n2026-05-30T07:22:50.6978731Z:Error: System.AggregateException: One or more errors occurred. ---> MS.Ess.EsrpClient.Sign.Exceptions.EsrpDigestSignExecuteProcessFailedException: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\p1kqilux.doz\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\p1kqilux.doz\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass27_0.<ExecuteDigestSignOperation>b__1()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.<>c__DisplayClass1.<ExecuteAction>b__0()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.ExecuteAction[TResult](Func`1 func)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.ExecuteDigestSignOperation(Input digestSignInput, Operation digestSignOperation, SignOperationData fileEntry, SignStatusResponse completionResponse, SignCommandDefinition param, Guid correlationId, String correlationVector, CancellationToken cancellationToken, String requestId, String tmpDestinationLocation)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.DigestSignInternal(Input digestSignInput, SignCommandDefinition param, Guid& correlationId, String correlationVector, CancellationToken cancellationToken, String groupId, SignOperationData fileEntry, Int32& signedFileCount, Int32& certRefreshCount, ConcurrentBag`1 completionResponses, RetryPolicy`1 operationRetryPolicy, String dynamicCertificateFile, String dynamicCertificateThumbprint, String requestId)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass23_3.<DigestSignAsync>b__6(SignOperationData fileEntry)\r\n --- End of inner exception stack trace ---\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<DigestSignAsync>d__23.MoveNext()\r\n---> (Inner Exception #0) MS.Ess.EsrpClient.Sign.Exceptions.EsrpDigestSignExecuteProcessFailedException: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\p1kqilux.doz\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\p1kqilux.doz\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass27_0.<ExecuteDigestSignOperation>b__1()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.<>c__DisplayClass1.<ExecuteAction>b__0()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.ExecuteAction[TResult](Func`1 func)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.ExecuteDigestSignOperation(Input digestSignInput, Operation digestSignOperation, SignOperationData fileEntry, SignStatusResponse completionResponse, SignCommandDefinition param, Guid correlationId, String correlationVector, CancellationToken cancellationToken, String requestId, String tmpDestinationLocation)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.DigestSignInternal(Input digestSignInput, SignCommandDefinition param, Guid& correlationId, String correlationVector, CancellationToken cancellationToken, String groupId, SignOperationData fileEntry, Int32& signedFileCount, Int32& certRefreshCount, ConcurrentBag`1 completionResponses, RetryPolicy`1 operationRetryPolicy, String dynamicCertificateFile, String dynamicCertificateThumbprint, String requestId)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass23_3.<DigestSignAsync>b__6(SignOperationData fileEntry)<---\r\n\r\n2026-05-30T07:22:50.7122620Z:Client Telemetry: Events published in this batch: 1\r\n2026-05-30T07:22:50.7122620Z:Client Telemetry: Events received so far in this session: 8\r\n2026-05-30T07:22:50.7122620Z:Client Telemetry: Events published so far in this session: 8\r\n2026-05-30T07:22:50.7334212Z:Constructing EsrpClientResponse\r\n2026-05-30T07:22:50.7340430Z:EsrpClientResponse is constructed\r\n2026-05-30T07:22:50.7664832Z:OperationDurationMs: 5354\r\n2026-05-30T07:22:50.7664832Z:DynamicCertGenerationTimeMs: 0\r\n2026-05-30T07:22:50.7664832Z:TimeToGetStorageShradsMs: 0\r\n2026-05-30T07:22:50.7664832Z:TotalScanSubmissionTimeMs: 0\r\n2026-05-30T07:22:50.7664832Z:ThrottleCount: 0\r\n2026-05-30T07:22:50.7664832Z:ThrottledTimeInSec: 0\r\n2026-05-30T07:22:50.7664832Z:\r\n2026-05-30T07:22:50.7674498Z:0/1 files signed in 00:00:05.354 (0 files/s, total number of certs refreshed: 0)\r\n2026-05-30T07:22:50.7700420Z:result json is: {\r\n \"submissionResponses\": [\r\n {\r\n \"fileStatusDetail\": [\r\n {\r\n \"sourceHash\": \"slqPswwGm8Qv8ditfd9rlJ+L4Mruoo5bkGd2V2sZqSk=\",\r\n \"hashType\": \"sha256\",\r\n \"destinationHash\": null,\r\n \"certificateThumbprint\": null,\r\n \"destinationLocation\": \"c:\\\\Temp\\\\AzureTemp\\\\p1kqilux.doz\\\\manifest.cat\",\r\n \"destinationFileSizeInBytes\": 0,\r\n \"sourceLocation\": \"c:\\\\Temp\\\\AzureTemp\\\\p1kqilux.doz\\\\manifest.cat\"\r\n }\r\n ],\r\n \"operationId\": \"e95f5f39-e72a-4ffd-91c9-8d6f94186620\",\r\n \"customerCorrelationId\": \"af9105ef-bb2c-4a78-83db-b4981648bb23\",\r\n \"statusCode\": \"failCanRetry\",\r\n \"errorInfo\": {\r\n \"code\": \"3138\",\r\n \"details\": {\r\n \"operation\": \"c:\\\\AT\\\\sitesroot\\\\0\\\\bin\\\\Plugins\\\\ESRPClient\\\\Win10.x86\\\\signtool.exe verify /pa /tw \\\"c:\\\\Temp\\\\AzureTemp\\\\p1kqilux.doz\\\\manifest.cat\\\"\",\r\n \"exitCode\": \"1\",\r\n \"stdOut\": \"File: c:\\\\Temp\\\\AzureTemp\\\\p1kqilux.doz\\\\manifest.cat\\r\\nIndex Algorithm Timestamp \\r\\n========================================\\r\\n\\r\\nNumber of errors: 1\\r\\n\\r\\n\\r\\n\",\r\n \"stdErr\": \"SignTool Error: A certificate chain processed, but terminated in a root\\r\\n\\tcertificate which is not trusted by the trust provider.\\r\\n\\r\\n\"\r\n },\r\n \"innerError\": null\r\n }\r\n }\r\n ],\r\n \"esrpClientSessionGuid\": \"03875b26-f409-451f-a3ec-e4ab39621ce7\",\r\n \"version\": \"1.0.0\"\r\n}\r\n2026-05-30T07:22:50.8114007Z:Warning: \r\n\r\n2026-05-30T07:22:50.8122308Z:esrp-client-finalTime: 9423.8964\r\n2026-05-30T07:22:50.8122308Z:Final return code is: 1\r\n2026-05-30T07:22:50.8122308Z:ClientTelemetryDispose started, this is not affecting Exe reliability, only lost telemetry if error happens ***********************\r\n2026-05-30T07:22:50.8169225Z:Client Telemetry: Flushing telemetry buffer with timeout 0 ms\r\n2026-05-30T07:22:50.8191014Z:Client Telemetry: Flushing telemetry buffer completed within timeout (true/false): False\r\n2026-05-30T07:22:50.8191014Z:Client Telemetry: Disposing telemetry buffer and event hub client\r\n2026-05-30T07:22:50.8256158Z:Client Telemetry: Events published in this batch: 3\r\n2026-05-30T07:22:50.8256158Z:Client Telemetry: Events received so far in this session: 11\r\n2026-05-30T07:22:50.8256158Z:Client Telemetry: Events published so far in this session: 11\r\n2026-05-30T07:22:50.8816542Z:event lost number: 0\r\n2026-05-30T07:22:50.8816542Z:total event processed number: 11\r\n2026-05-30T07:22:50.8816542Z:total event received number: 11\r\n2026-05-30T07:22:50.8816542Z:##EsrpClientTelemetry.TotalEventsProcessed##: 11\r\n2026-05-30T07:22:50.8816542Z:##EsrpClientTelemetry.TotalEventsReceived##: 11\r\n2026-05-30T07:22:50.8816542Z:##EsrpClientTelemetry.DisposeTimeMilliseconds##: 69\r\n2026-05-30T07:22:50.8816542Z:ClientTelemetryDispose ended ***********************\r\n","StdErr":"2026-05-30T07:22:50.6978731Z:Error: System.AggregateException: One or more errors occurred. ---> MS.Ess.EsrpClient.Sign.Exceptions.EsrpDigestSignExecuteProcessFailedException: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\p1kqilux.doz\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\p1kqilux.doz\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass27_0.<ExecuteDigestSignOperation>b__1()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.<>c__DisplayClass1.<ExecuteAction>b__0()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.ExecuteAction[TResult](Func`1 func)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.ExecuteDigestSignOperation(Input digestSignInput, Operation digestSignOperation, SignOperationData fileEntry, SignStatusResponse completionResponse, SignCommandDefinition param, Guid correlationId, String correlationVector, CancellationToken cancellationToken, String requestId, String tmpDestinationLocation)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.DigestSignInternal(Input digestSignInput, SignCommandDefinition param, Guid& correlationId, String correlationVector, CancellationToken cancellationToken, String groupId, SignOperationData fileEntry, Int32& signedFileCount, Int32& certRefreshCount, ConcurrentBag`1 completionResponses, RetryPolicy`1 operationRetryPolicy, String dynamicCertificateFile, String dynamicCertificateThumbprint, String requestId)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass23_3.<DigestSignAsync>b__6(SignOperationData fileEntry)\r\n --- End of inner exception stack trace ---\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<DigestSignAsync>d__23.MoveNext()\r\n---> (Inner Exception #0) MS.Ess.EsrpClient.Sign.Exceptions.EsrpDigestSignExecuteProcessFailedException: Operation: c:\\AT\\sitesroot\\0\\bin\\Plugins\\ESRPClient\\Win10.x86\\signtool.exe verify /pa /tw \"c:\\Temp\\AzureTemp\\p1kqilux.doz\\manifest.cat\" failed\r\nExit Code: 1\r\nStdOut:\r\nFile: c:\\Temp\\AzureTemp\\p1kqilux.doz\\manifest.cat\r\nIndex Algorithm Timestamp \r\n========================================\r\n\r\nNumber of errors: 1\r\n\r\n\r\n\r\nStdErr:\r\nSignTool Error: A certificate chain processed, but terminated in a root\r\n\tcertificate which is not trusted by the trust provider.\r\n\r\n\r\n\r\n\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass27_0.<ExecuteDigestSignOperation>b__1()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.<>c__DisplayClass1.<ExecuteAction>b__0()\r\n at Microsoft.Practices.EnterpriseLibrary.TransientFaultHandling.RetryPolicy.ExecuteAction[TResult](Func`1 func)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.ExecuteDigestSignOperation(Input digestSignInput, Operation digestSignOperation, SignOperationData fileEntry, SignStatusResponse completionResponse, SignCommandDefinition param, Guid correlationId, String correlationVector, CancellationToken cancellationToken, String requestId, String tmpDestinationLocation)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.DigestSignInternal(Input digestSignInput, SignCommandDefinition param, Guid& correlationId, String correlationVector, CancellationToken cancellationToken, String groupId, SignOperationData fileEntry, Int32& signedFileCount, Int32& certRefreshCount, ConcurrentBag`1 completionResponses, RetryPolicy`1 operationRetryPolicy, String dynamicCertificateFile, String dynamicCertificateThumbprint, String requestId)\r\n at MS.Ess.EsrpClient.Sign.SignHandlers.SignJobHandler.<>c__DisplayClass23_3.<DigestSignAsync>b__6(SignOperationData fileEntry)<---\r\n\r\n","ExitCode":1,"RunningTime":"00:00:10.2845531"} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
| #!/usr/bin/env node | ||
| /*--------------------------------------------------------------------------------------------- | ||
| * Copyright (c) Microsoft Corporation. All rights reserved. | ||
| *--------------------------------------------------------------------------------------------*/ | ||
| import{spawnSync as o}from"node:child_process";import{fileURLToPath as i}from"node:url";import{isNonGlibcLinuxSync as e}from"detect-libc";async function a(){for(const t of c())try{const r=i(import.meta.resolve(`@github/copilot-${t}-${process.arch}`)),n=o(r,process.argv.slice(2),{stdio:"inherit"});process.exit(s(n,r))}catch{}console.error("GitHub Copilot CLI: no platform package found. Reinstall with `npm install -g @github/copilot` to fetch the package for your platform."),process.exit(1)}a().catch(()=>{});function s(t,r){if(t.error)throw t.error;if(t.signal)throw process.stderr.write(`GitHub Copilot native binary at ${r} was terminated by signal ${t.signal}. | ||
| `),new Error(`Native binary terminated by signal ${t.signal}`);return t.status??1}function c(){return process.platform==="linux"?l()?["linuxmusl","linux"]:["linux"]:[process.platform]}function l(){return e()}export{s as handleSpawnResult}; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
| /*--------------------------------------------------------------------------------------------- | ||
| * Copyright (c) Microsoft Corporation. All rights reserved. | ||
| *--------------------------------------------------------------------------------------------*/ | ||
| // Bootstrap script for extension subprocesses. | ||
| // This is the fork target — it sets up the SDK resolver in-process, | ||
| // then dynamically imports the user's extension via the EXTENSION_PATH env var. | ||
| import Module from "node:module"; | ||
| import { resolve as resolvePath } from "node:path"; | ||
| import { pathToFileURL } from "node:url"; | ||
| // Register the ESM SDK resolver hook before loading the extension | ||
| Module.register("./extension_sdk_resolver.mjs", import.meta.url); | ||
| // Register a CJS require hook so that CommonJS extensions can | ||
| // `require("@github/copilot-sdk")` and have it resolve to the bundled SDK. | ||
| const sdkPath = process.env.COPILOT_SDK_PATH; | ||
| process.stderr.write( | ||
| `[extension-bootstrap] starting: pid=${process.pid}, COPILOT_SDK_PATH=${sdkPath ?? "<unset>"}, EXTENSION_PATH=${process.env.EXTENSION_PATH ?? "<unset>"}, SESSION_ID=${process.env.SESSION_ID ?? "<unset>"}\n`, | ||
| ); | ||
| if (sdkPath) { | ||
| const originalResolveFilename = Module._resolveFilename; | ||
| Module._resolveFilename = function (request, parent, isMain, options) { | ||
| if (request === "@github/copilot-sdk") { | ||
| return resolvePath(sdkPath, "index.js"); | ||
| } | ||
| if (request === "@github/copilot-sdk/extension") { | ||
| return resolvePath(sdkPath, "extension.js"); | ||
| } | ||
| return originalResolveFilename.call(this, request, parent, isMain, options); | ||
| }; | ||
| } | ||
| // Load the user's extension | ||
| const extensionPath = process.env.EXTENSION_PATH; | ||
| if (!extensionPath) { | ||
| console.error("[extension-bootstrap] No extension path provided"); | ||
| process.exit(1); | ||
| } | ||
| try { | ||
| process.stderr.write(`[extension-bootstrap] importing extension: ${extensionPath}\n`); | ||
| await import(pathToFileURL(extensionPath).href); | ||
| } catch (err) { | ||
| console.error(`[extension-bootstrap] Failed to load extension: ${err}`); | ||
| process.exit(1); | ||
| } |
| /*--------------------------------------------------------------------------------------------- | ||
| * Copyright (c) Microsoft Corporation. All rights reserved. | ||
| *--------------------------------------------------------------------------------------------*/ | ||
| // ESM loader hook for extension subprocesses. | ||
| // Intercepts `@github/copilot-sdk` imports and redirects them to the | ||
| // CLI's bundled copies. | ||
| import { resolve as resolvePath } from "node:path"; | ||
| import { pathToFileURL } from "node:url"; | ||
| // COPILOT_SDK_PATH points to the directory containing the bundled SDK files. | ||
| const sdkPath = process.env.COPILOT_SDK_PATH; | ||
| process.stderr.write( | ||
| `[extension-resolver] resolver hook loaded: pid=${process.pid}, COPILOT_SDK_PATH=${sdkPath ?? "<unset>"}\n`, | ||
| ); | ||
| export async function resolve(specifier, context, nextResolve) { | ||
| if (!sdkPath) { | ||
| return nextResolve(specifier, context); | ||
| } | ||
| if (specifier === "@github/copilot-sdk") { | ||
| const resolved = pathToFileURL(resolvePath(sdkPath, "index.js")).href; | ||
| return { url: resolved, shortCircuit: true }; | ||
| } | ||
| if (specifier === "@github/copilot-sdk/extension") { | ||
| const resolved = pathToFileURL(resolvePath(sdkPath, "extension.js")).href; | ||
| return { url: resolved, shortCircuit: true }; | ||
| } | ||
| return nextResolve(specifier, context); | ||
| } |
| // | ||
| // Copyright 2021-2022 Picovoice Inc. | ||
| // | ||
| // You may not use this file except in compliance with the license. A copy of the license is located in the "LICENSE" | ||
| // file accompanying this source. | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on | ||
| // an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| // specific language governing permissions and limitations under the License. | ||
| // | ||
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| const pv_recorder_status_t_1 = require("./pv_recorder_status_t"); | ||
| class PvRecorderStatusOutOfMemoryError extends Error { | ||
| } | ||
| class PvRecorderStatusInvalidArgumentError extends Error { | ||
| } | ||
| class PvRecorderStatusInvalidStateError extends Error { | ||
| } | ||
| class PvRecorderStatusBackendError extends Error { | ||
| } | ||
| class PvRecorderStatusDeviceAlreadyInitializedError extends Error { | ||
| } | ||
| class PvRecorderStatusDeviceNotInitializedError extends Error { | ||
| } | ||
| class PvRecorderStatusIOError extends Error { | ||
| } | ||
| class PvRecorderStatusRuntimeError extends Error { | ||
| } | ||
| function pvRecorderStatusToException(status, errorMessage) { | ||
| switch (status) { | ||
| case pv_recorder_status_t_1.default.OUT_OF_MEMORY: | ||
| return new PvRecorderStatusOutOfMemoryError(errorMessage); | ||
| case pv_recorder_status_t_1.default.INVALID_ARGUMENT: | ||
| return new PvRecorderStatusInvalidArgumentError(errorMessage); | ||
| case pv_recorder_status_t_1.default.INVALID_STATE: | ||
| return new PvRecorderStatusInvalidStateError(errorMessage); | ||
| case pv_recorder_status_t_1.default.BACKEND_ERROR: | ||
| return new PvRecorderStatusBackendError(errorMessage); | ||
| case pv_recorder_status_t_1.default.DEVICE_ALREADY_INITIALIZED: | ||
| return new PvRecorderStatusDeviceAlreadyInitializedError(errorMessage); | ||
| case pv_recorder_status_t_1.default.DEVICE_NOT_INITIALIZED: | ||
| return new PvRecorderStatusDeviceNotInitializedError(errorMessage); | ||
| case pv_recorder_status_t_1.default.IO_ERROR: | ||
| return new PvRecorderStatusIOError(errorMessage); | ||
| case pv_recorder_status_t_1.default.RUNTIME_ERROR: | ||
| return new PvRecorderStatusRuntimeError(errorMessage); | ||
| default: | ||
| // eslint-disable-next-line | ||
| console.warn(`Unknown error code: ${status}`); | ||
| return new Error(errorMessage); | ||
| } | ||
| } | ||
| exports.default = pvRecorderStatusToException; | ||
| //# sourceMappingURL=errors.js.map |
| // | ||
| // Copyright 2021-2022 Picovoice Inc. | ||
| // | ||
| // You may not use this file except in compliance with the license. A copy of the license is located in the "LICENSE" | ||
| // file accompanying this source. | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on | ||
| // an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| // specific language governing permissions and limitations under the License. | ||
| // | ||
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.PvRecorder = void 0; | ||
| const pv_recorder_1 = require("./pv_recorder"); | ||
| exports.PvRecorder = pv_recorder_1.default; | ||
| //# sourceMappingURL=index.js.map |
| // | ||
| // Copyright 2025 Picovoice Inc. | ||
| // | ||
| // You may not use this file except in compliance with the license. A copy of the license is located in the "LICENSE" | ||
| // file accompanying this source. | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on | ||
| // an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| // specific language governing permissions and limitations under the License. | ||
| // | ||
| 'use strict'; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| exports.getSystemLibraryPath = exports.getPlatform = void 0; | ||
| const fs = require("fs"); | ||
| const os = require("os"); | ||
| const path = require("path"); | ||
| const SYSTEM_LINUX = 'linux'; | ||
| const SYSTEM_MAC = 'darwin'; | ||
| const SYSTEM_WINDOWS = 'win32'; | ||
| const X86_64 = 'x64'; | ||
| const ARM_32 = 'arm'; | ||
| const ARM_64 = 'arm64'; | ||
| const PLATFORM_LINUX = 'linux'; | ||
| const PLATFORM_MAC = 'mac'; | ||
| const PLATFORM_RASPBERRY_PI = 'raspberry-pi'; | ||
| const PLATFORM_WINDOWS = 'windows'; | ||
| const ARM_CPU_64 = '-aarch64'; | ||
| const ARM_CPU_CORTEX_A53 = 'cortex-a53'; | ||
| const ARM_CPU_CORTEX_A72 = 'cortex-a72'; | ||
| const ARM_CPU_CORTEX_A76 = 'cortex-a76'; | ||
| const SUPPORTED_NODEJS_SYSTEMS = new Set([ | ||
| SYSTEM_LINUX, | ||
| SYSTEM_MAC, | ||
| SYSTEM_WINDOWS, | ||
| ]); | ||
| const LIBRARY_PATH_PREFIX = '../lib/'; | ||
| const SYSTEM_TO_LIBRARY_PATH = new Map(); | ||
| SYSTEM_TO_LIBRARY_PATH.set(`${SYSTEM_MAC}/${X86_64}`, `${PLATFORM_MAC}/x86_64/pv_recorder.node`); | ||
| SYSTEM_TO_LIBRARY_PATH.set(`${SYSTEM_MAC}/${ARM_64}`, `${PLATFORM_MAC}/arm64/pv_recorder.node`); | ||
| SYSTEM_TO_LIBRARY_PATH.set(`${SYSTEM_LINUX}/${X86_64}`, `${PLATFORM_LINUX}/x86_64/pv_recorder.node`); | ||
| SYSTEM_TO_LIBRARY_PATH.set(`${SYSTEM_LINUX}/${ARM_CPU_CORTEX_A53}`, `${PLATFORM_RASPBERRY_PI}/${ARM_CPU_CORTEX_A53}/pv_recorder.node`); | ||
| SYSTEM_TO_LIBRARY_PATH.set(`${SYSTEM_LINUX}/${ARM_CPU_CORTEX_A53}${ARM_CPU_64}`, `${PLATFORM_RASPBERRY_PI}/${ARM_CPU_CORTEX_A53}${ARM_CPU_64}/pv_recorder.node`); | ||
| SYSTEM_TO_LIBRARY_PATH.set(`${SYSTEM_LINUX}/${ARM_CPU_CORTEX_A72}`, `${PLATFORM_RASPBERRY_PI}/${ARM_CPU_CORTEX_A72}/pv_recorder.node`); | ||
| SYSTEM_TO_LIBRARY_PATH.set(`${SYSTEM_LINUX}/${ARM_CPU_CORTEX_A72}${ARM_CPU_64}`, `${PLATFORM_RASPBERRY_PI}/${ARM_CPU_CORTEX_A72}${ARM_CPU_64}/pv_recorder.node`); | ||
| SYSTEM_TO_LIBRARY_PATH.set(`${SYSTEM_LINUX}/${ARM_CPU_CORTEX_A76}`, `${PLATFORM_RASPBERRY_PI}/${ARM_CPU_CORTEX_A76}/pv_recorder.node`); | ||
| SYSTEM_TO_LIBRARY_PATH.set(`${SYSTEM_LINUX}/${ARM_CPU_CORTEX_A76}${ARM_CPU_64}`, `${PLATFORM_RASPBERRY_PI}/${ARM_CPU_CORTEX_A76}${ARM_CPU_64}/pv_recorder.node`); | ||
| SYSTEM_TO_LIBRARY_PATH.set(`${SYSTEM_WINDOWS}/${X86_64}`, `${PLATFORM_WINDOWS}/amd64/pv_recorder.node`); | ||
| SYSTEM_TO_LIBRARY_PATH.set(`${SYSTEM_WINDOWS}/${ARM_64}`, `${PLATFORM_WINDOWS}/arm64/pv_recorder.node`); | ||
| function absoluteLibraryPath(libraryPath) { | ||
| return path.resolve(__dirname, LIBRARY_PATH_PREFIX, libraryPath); | ||
| } | ||
| function getCpuPart() { | ||
| const cpuInfo = fs.readFileSync('/proc/cpuinfo', 'ascii'); | ||
| for (const infoLine of cpuInfo.split('\n')) { | ||
| if (infoLine.includes('CPU part')) { | ||
| const infoLineSplit = infoLine.split(' '); | ||
| return infoLineSplit[infoLineSplit.length - 1].toLowerCase(); | ||
| } | ||
| } | ||
| throw new Error(`Unsupported CPU.`); | ||
| } | ||
| function getLinuxPlatform() { | ||
| const cpuPart = getCpuPart(); | ||
| switch (cpuPart) { | ||
| case '0xd03': | ||
| case '0xd08': | ||
| case '0xd0b': | ||
| return PLATFORM_RASPBERRY_PI; | ||
| default: | ||
| throw new Error(`Unsupported CPU: '${cpuPart}'`); | ||
| } | ||
| } | ||
| function getLinuxMachine(arch) { | ||
| let archInfo = ''; | ||
| if (arch === ARM_64) { | ||
| archInfo = ARM_CPU_64; | ||
| } | ||
| const cpuPart = getCpuPart(); | ||
| switch (cpuPart) { | ||
| case '0xd03': | ||
| return ARM_CPU_CORTEX_A53 + archInfo; | ||
| case '0xd08': | ||
| return ARM_CPU_CORTEX_A72 + archInfo; | ||
| case '0xd0b': | ||
| return ARM_CPU_CORTEX_A76 + archInfo; | ||
| default: | ||
| throw new Error(`Unsupported CPU: '${cpuPart}'`); | ||
| } | ||
| } | ||
| function getPlatform() { | ||
| const system = os.platform(); | ||
| const arch = os.arch(); | ||
| if (system === SYSTEM_MAC && (arch === X86_64 || arch === ARM_64)) { | ||
| return PLATFORM_MAC; | ||
| } | ||
| if (system === SYSTEM_WINDOWS && (arch === X86_64 || arch === ARM_64)) { | ||
| return PLATFORM_WINDOWS; | ||
| } | ||
| if (system === SYSTEM_LINUX) { | ||
| if (arch === X86_64) { | ||
| return PLATFORM_LINUX; | ||
| } | ||
| return getLinuxPlatform(); | ||
| } | ||
| throw `System ${system}/${arch} is not supported by this library.`; | ||
| } | ||
| exports.getPlatform = getPlatform; | ||
| function getSystemLibraryPath() { | ||
| const system = os.platform(); | ||
| const arch = os.arch(); | ||
| if (SUPPORTED_NODEJS_SYSTEMS.has(system)) { | ||
| switch (system) { | ||
| case SYSTEM_MAC: { | ||
| if (arch === X86_64) { | ||
| return absoluteLibraryPath(SYSTEM_TO_LIBRARY_PATH.get(`${SYSTEM_MAC}/${X86_64}`)); | ||
| } | ||
| else if (arch === ARM_64) { | ||
| return absoluteLibraryPath(SYSTEM_TO_LIBRARY_PATH.get(`${SYSTEM_MAC}/${ARM_64}`)); | ||
| } | ||
| break; | ||
| } | ||
| case SYSTEM_LINUX: { | ||
| if (arch === X86_64) { | ||
| return absoluteLibraryPath(SYSTEM_TO_LIBRARY_PATH.get(`${SYSTEM_LINUX}/${X86_64}`)); | ||
| } | ||
| else if (arch === ARM_32 || arch === ARM_64) { | ||
| const linuxMachine = getLinuxMachine(arch); | ||
| if (linuxMachine !== null) { | ||
| return absoluteLibraryPath(SYSTEM_TO_LIBRARY_PATH.get(`${SYSTEM_LINUX}/${linuxMachine}`)); | ||
| } | ||
| throw new Error(`System ${system}/${arch} is not supported by this library for this CPU.`); | ||
| } | ||
| break; | ||
| } | ||
| case SYSTEM_WINDOWS: { | ||
| if (arch === X86_64 || arch === ARM_64) { | ||
| return absoluteLibraryPath(SYSTEM_TO_LIBRARY_PATH.get(`${SYSTEM_WINDOWS}/${arch}`)); | ||
| } | ||
| break; | ||
| } | ||
| default: { | ||
| throw new Error(`System ${system}/${arch} is not supported by this library.`); | ||
| } | ||
| } | ||
| } | ||
| throw new Error(`System ${system}/${arch} is not supported by this library.`); | ||
| } | ||
| exports.getSystemLibraryPath = getSystemLibraryPath; | ||
| //# sourceMappingURL=platforms.js.map |
| // | ||
| // Copyright 2021-2022 Picovoice Inc. | ||
| // | ||
| // You may not use this file except in compliance with the license. A copy of the license is located in the "LICENSE" | ||
| // file accompanying this source. | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on | ||
| // an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| // specific language governing permissions and limitations under the License. | ||
| // | ||
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| var PvRecorderStatus; | ||
| (function (PvRecorderStatus) { | ||
| PvRecorderStatus[PvRecorderStatus["SUCCESS"] = 0] = "SUCCESS"; | ||
| PvRecorderStatus[PvRecorderStatus["OUT_OF_MEMORY"] = 1] = "OUT_OF_MEMORY"; | ||
| PvRecorderStatus[PvRecorderStatus["INVALID_ARGUMENT"] = 2] = "INVALID_ARGUMENT"; | ||
| PvRecorderStatus[PvRecorderStatus["INVALID_STATE"] = 3] = "INVALID_STATE"; | ||
| PvRecorderStatus[PvRecorderStatus["BACKEND_ERROR"] = 4] = "BACKEND_ERROR"; | ||
| PvRecorderStatus[PvRecorderStatus["DEVICE_ALREADY_INITIALIZED"] = 5] = "DEVICE_ALREADY_INITIALIZED"; | ||
| PvRecorderStatus[PvRecorderStatus["DEVICE_NOT_INITIALIZED"] = 6] = "DEVICE_NOT_INITIALIZED"; | ||
| PvRecorderStatus[PvRecorderStatus["IO_ERROR"] = 7] = "IO_ERROR"; | ||
| PvRecorderStatus[PvRecorderStatus["RUNTIME_ERROR"] = 8] = "RUNTIME_ERROR"; | ||
| })(PvRecorderStatus || (PvRecorderStatus = {})); | ||
| exports.default = PvRecorderStatus; | ||
| //# sourceMappingURL=pv_recorder_status_t.js.map |
| // | ||
| // Copyright 2022-2023 Picovoice Inc. | ||
| // | ||
| // You may not use this file except in compliance with the license. A copy of the license is located in the "LICENSE" | ||
| // file accompanying this source. | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on | ||
| // an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the | ||
| // specific language governing permissions and limitations under the License. | ||
| // | ||
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { value: true }); | ||
| const pv_recorder_status_t_1 = require("./pv_recorder_status_t"); | ||
| const errors_1 = require("./errors"); | ||
| const platforms_1 = require("./platforms"); | ||
| /** | ||
| * PvRecorder class for recording audio. | ||
| */ | ||
| class PvRecorder { | ||
| // eslint-disable-next-line | ||
| static _pvRecorder = require((0, platforms_1.getSystemLibraryPath)()); | ||
| _handle; | ||
| _frameLength; | ||
| _sampleRate; | ||
| _version; | ||
| /** | ||
| * PvRecorder constructor. | ||
| * | ||
| * @param frameLength Length of the audio frames to receive per read call. | ||
| * @param deviceIndex The audio device index to use to record audio. A value of (-1) will use machine's default audio device. | ||
| * @param bufferedFramesCount The number of audio frames buffered internally for reading - i.e. internal circular buffer | ||
| * will be of size `frameLength` * `bufferedFramesCount`. If this value is too low, buffer overflows could occur | ||
| * and audio frames could be dropped. A higher value will increase memory usage. | ||
| */ | ||
| constructor(frameLength, deviceIndex = -1, bufferedFramesCount = 50) { | ||
| let pvRecorderHandleAndStatus; | ||
| try { | ||
| pvRecorderHandleAndStatus = PvRecorder._pvRecorder.init(frameLength, deviceIndex, bufferedFramesCount); | ||
| } | ||
| catch (err) { | ||
| (0, errors_1.default)(err.code, err); | ||
| } | ||
| const status = pvRecorderHandleAndStatus.status; | ||
| if (status !== pv_recorder_status_t_1.default.SUCCESS) { | ||
| throw (0, errors_1.default)(status, "PvRecorder failed to initialize."); | ||
| } | ||
| this._handle = pvRecorderHandleAndStatus.handle; | ||
| this._frameLength = frameLength; | ||
| this._sampleRate = PvRecorder._pvRecorder.sample_rate(); | ||
| this._version = PvRecorder._pvRecorder.version(); | ||
| } | ||
| /** | ||
| * @returns Length of the audio frames to receive per read call. | ||
| */ | ||
| get frameLength() { | ||
| return this._frameLength; | ||
| } | ||
| /** | ||
| * @returns Audio sample rate used by PvRecorder. | ||
| */ | ||
| get sampleRate() { | ||
| return this._sampleRate; | ||
| } | ||
| /** | ||
| * @returns the version of the PvRecorder | ||
| */ | ||
| get version() { | ||
| return this._version; | ||
| } | ||
| /** | ||
| * @returns Whether PvRecorder is currently recording audio or not. | ||
| */ | ||
| get isRecording() { | ||
| return PvRecorder._pvRecorder.get_is_recording(this._handle); | ||
| } | ||
| /** | ||
| * Starts recording audio. | ||
| */ | ||
| start() { | ||
| const status = PvRecorder._pvRecorder.start(this._handle); | ||
| if (status !== pv_recorder_status_t_1.default.SUCCESS) { | ||
| throw (0, errors_1.default)(status, "PvRecorder failed to start."); | ||
| } | ||
| } | ||
| /** | ||
| * Stops recording audio. | ||
| */ | ||
| stop() { | ||
| const status = PvRecorder._pvRecorder.stop(this._handle); | ||
| if (status !== pv_recorder_status_t_1.default.SUCCESS) { | ||
| throw (0, errors_1.default)(status, "PvRecorder failed to stop."); | ||
| } | ||
| } | ||
| /** | ||
| * Asynchronous call to read a frame of audio data. | ||
| * | ||
| * @returns {Promise<Int16Array>} Audio data frame. | ||
| */ | ||
| async read() { | ||
| return new Promise((resolve, reject) => { | ||
| setTimeout(() => { | ||
| const pcm = new Int16Array(this._frameLength); | ||
| const status = PvRecorder._pvRecorder.read(this._handle, pcm); | ||
| if (status !== pv_recorder_status_t_1.default.SUCCESS) { | ||
| reject((0, errors_1.default)(status, "PvRecorder failed to read audio data frame.")); | ||
| } | ||
| resolve(pcm); | ||
| }); | ||
| }); | ||
| } | ||
| /** | ||
| * Synchronous call to read a frame of audio data. | ||
| * | ||
| * @returns {Int16Array} Audio data frame. | ||
| */ | ||
| readSync() { | ||
| const pcm = new Int16Array(this._frameLength); | ||
| const status = PvRecorder._pvRecorder.read(this._handle, pcm); | ||
| if (status !== pv_recorder_status_t_1.default.SUCCESS) { | ||
| throw (0, errors_1.default)(status, "PvRecorder failed to read audio data frame."); | ||
| } | ||
| return pcm; | ||
| } | ||
| /** | ||
| * Enable or disable debug logging for PvRecorder. Debug logs will indicate when there are overflows in the internal | ||
| * frame buffer and when an audio source is generating frames of silence. | ||
| * | ||
| * @param isDebugLoggingEnabled Boolean indicating whether the debug logging is enabled or disabled. | ||
| */ | ||
| setDebugLogging(isDebugLoggingEnabled) { | ||
| PvRecorder._pvRecorder.set_debug_logging(this._handle, isDebugLoggingEnabled); | ||
| } | ||
| /** | ||
| * Returns the name of the selected device used to capture audio. | ||
| * | ||
| * @returns {string} Name of the selected audio device. | ||
| */ | ||
| getSelectedDevice() { | ||
| const device = PvRecorder._pvRecorder.get_selected_device(this._handle); | ||
| if ((device === undefined) || (device === null)) { | ||
| throw new Error("Failed to get selected device."); | ||
| } | ||
| return device; | ||
| } | ||
| /** | ||
| * Destructor. Releases resources acquired by PvRecorder. | ||
| */ | ||
| release() { | ||
| PvRecorder._pvRecorder.delete(this._handle); | ||
| } | ||
| /** | ||
| * Helper function to get the list of available audio devices. | ||
| * | ||
| * @returns {Array<string>} An array of the available device names. | ||
| */ | ||
| static getAvailableDevices() { | ||
| const devices = PvRecorder._pvRecorder.get_available_devices(); | ||
| if ((devices === undefined) || (devices === null)) { | ||
| throw new Error("Failed to get audio devices."); | ||
| } | ||
| return devices; | ||
| } | ||
| } | ||
| exports.default = PvRecorder; | ||
| //# sourceMappingURL=pv_recorder.js.map |
| import PvRecorderStatus from "./pv_recorder_status_t"; | ||
| declare function pvRecorderStatusToException(status: PvRecorderStatus, errorMessage: string): Error; | ||
| export default pvRecorderStatusToException; | ||
| //# sourceMappingURL=errors.d.ts.map |
| import PvRecorder from "./pv_recorder"; | ||
| export { PvRecorder }; | ||
| //# sourceMappingURL=index.d.ts.map |
| export declare function getPlatform(): string; | ||
| export declare function getSystemLibraryPath(): string; | ||
| //# sourceMappingURL=platforms.d.ts.map |
| declare enum PvRecorderStatus { | ||
| SUCCESS = 0, | ||
| OUT_OF_MEMORY = 1, | ||
| INVALID_ARGUMENT = 2, | ||
| INVALID_STATE = 3, | ||
| BACKEND_ERROR = 4, | ||
| DEVICE_ALREADY_INITIALIZED = 5, | ||
| DEVICE_NOT_INITIALIZED = 6, | ||
| IO_ERROR = 7, | ||
| RUNTIME_ERROR = 8 | ||
| } | ||
| export default PvRecorderStatus; | ||
| //# sourceMappingURL=pv_recorder_status_t.d.ts.map |
| /** | ||
| * PvRecorder class for recording audio. | ||
| */ | ||
| declare class PvRecorder { | ||
| private static _pvRecorder; | ||
| private readonly _handle; | ||
| private readonly _frameLength; | ||
| private readonly _sampleRate; | ||
| private readonly _version; | ||
| /** | ||
| * PvRecorder constructor. | ||
| * | ||
| * @param frameLength Length of the audio frames to receive per read call. | ||
| * @param deviceIndex The audio device index to use to record audio. A value of (-1) will use machine's default audio device. | ||
| * @param bufferedFramesCount The number of audio frames buffered internally for reading - i.e. internal circular buffer | ||
| * will be of size `frameLength` * `bufferedFramesCount`. If this value is too low, buffer overflows could occur | ||
| * and audio frames could be dropped. A higher value will increase memory usage. | ||
| */ | ||
| constructor(frameLength: number, deviceIndex?: number, bufferedFramesCount?: number); | ||
| /** | ||
| * @returns Length of the audio frames to receive per read call. | ||
| */ | ||
| get frameLength(): number; | ||
| /** | ||
| * @returns Audio sample rate used by PvRecorder. | ||
| */ | ||
| get sampleRate(): number; | ||
| /** | ||
| * @returns the version of the PvRecorder | ||
| */ | ||
| get version(): string; | ||
| /** | ||
| * @returns Whether PvRecorder is currently recording audio or not. | ||
| */ | ||
| get isRecording(): boolean; | ||
| /** | ||
| * Starts recording audio. | ||
| */ | ||
| start(): void; | ||
| /** | ||
| * Stops recording audio. | ||
| */ | ||
| stop(): void; | ||
| /** | ||
| * Asynchronous call to read a frame of audio data. | ||
| * | ||
| * @returns {Promise<Int16Array>} Audio data frame. | ||
| */ | ||
| read(): Promise<Int16Array>; | ||
| /** | ||
| * Synchronous call to read a frame of audio data. | ||
| * | ||
| * @returns {Int16Array} Audio data frame. | ||
| */ | ||
| readSync(): Int16Array; | ||
| /** | ||
| * Enable or disable debug logging for PvRecorder. Debug logs will indicate when there are overflows in the internal | ||
| * frame buffer and when an audio source is generating frames of silence. | ||
| * | ||
| * @param isDebugLoggingEnabled Boolean indicating whether the debug logging is enabled or disabled. | ||
| */ | ||
| setDebugLogging(isDebugLoggingEnabled: boolean): void; | ||
| /** | ||
| * Returns the name of the selected device used to capture audio. | ||
| * | ||
| * @returns {string} Name of the selected audio device. | ||
| */ | ||
| getSelectedDevice(): string; | ||
| /** | ||
| * Destructor. Releases resources acquired by PvRecorder. | ||
| */ | ||
| release(): void; | ||
| /** | ||
| * Helper function to get the list of available audio devices. | ||
| * | ||
| * @returns {Array<string>} An array of the available device names. | ||
| */ | ||
| static getAvailableDevices(): string[]; | ||
| } | ||
| export default PvRecorder; | ||
| //# sourceMappingURL=pv_recorder.d.ts.map |
| { | ||
| "name": "@picovoice/pvrecorder-node", | ||
| "version": "1.2.9", | ||
| "description": "Audio recorder sdk for Nodejs.", | ||
| "main": "dist/index.js", | ||
| "types": "dist/types", | ||
| "keywords": [ | ||
| "audio, audio recorder" | ||
| ], | ||
| "author": "Picovoice Inc.", | ||
| "license": "Apache-2.0", | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "https://github.com/Picovoice/pvrecorder.git", | ||
| "directory": "binding/nodejs" | ||
| }, | ||
| "scripts": { | ||
| "build": "npm-run-all --parallel build:**", | ||
| "build:all": "tsc", | ||
| "build:types": "tsc --declaration --declarationMap --emitDeclarationOnly --outDir ./dist/types", | ||
| "prepack": "npm run build", | ||
| "prepare": "node copy.js", | ||
| "test": "jest --no-cache", | ||
| "lint": "eslint . --ext .js,.ts" | ||
| }, | ||
| "devDependencies": { | ||
| "@types/jest": "^27.4.1", | ||
| "@types/node": "^17.0.21", | ||
| "@typescript-eslint/eslint-plugin": "^5.19.0", | ||
| "@typescript-eslint/parser": "^5.19.0", | ||
| "eslint": "^8.13.0", | ||
| "eslint-plugin-jest": "^27.1.6", | ||
| "jest": "^27.5.1", | ||
| "mkdirp": "^1.0.4", | ||
| "ncp": "^2.0.0", | ||
| "npm-run-all": "^4.1.5", | ||
| "prettier": "^2.6.2", | ||
| "ts-jest": "^27.1.3", | ||
| "typescript": "^4.6.2" | ||
| }, | ||
| "engines": { | ||
| "node": ">=18.0.0" | ||
| } | ||
| } |
| # PvRecorder Binding for Node.js | ||
| ## PvRecorder | ||
| PvRecorder is an easy-to-use, cross-platform audio recorder designed for real-time speech audio processing. It allows developers access to an audio device's input stream, broken up into data frames of a given size. | ||
| ## Compatibility | ||
| - Node.js 18+ | ||
| - Runs on Linux (x86_64), macOS (x86_64 and arm64), Windows (x86_64 and arm64), and Raspberry Pi (3, 4, 5). | ||
| ## Installation | ||
| ```console | ||
| yarn add @picovoice/pvrecorder-node | ||
| ``` | ||
| ## Usage | ||
| Initialize and begin recording: | ||
| ```javascript | ||
| const { PvRecorder } = require("@picovoice/pvrecorder-node"); | ||
| const recorder = new PvRecorder(/*frameLength*/ 512); | ||
| recorder.start() | ||
| ``` | ||
| (or) | ||
| Use `get_available_devices()` to get a list of available devices and then initialize the instance based on the index of a device: | ||
| ```javascript | ||
| const { PvRecorder } = require("@picovoice/pvrecorder-node"); | ||
| const devices = PvRecorder.getAvailableDevices() | ||
| const recorder = new PvRecorder(512, /*device index*/0); | ||
| recorder.start() | ||
| ``` | ||
| Read frames of audio: | ||
| ```javascript | ||
| while (recorder.isRecording) { | ||
| // const frame = recorder.readSync(), for synchronous calls | ||
| const frame = await recorder.read(); | ||
| // process audio frame | ||
| } | ||
| ``` | ||
| To stop recording, call `stop()` on the instance: | ||
| ```javascript | ||
| recorder.stop(); | ||
| ``` | ||
| Once you are done, free the resources acquired by PvRecorder. You do not have to call `stop()` before `release()`: | ||
| ```javascript | ||
| recorder.release(); | ||
| ``` | ||
| ## Demos | ||
| [@picovoice/pvrecorder-node-demo](https://www.npmjs.com/package/@picovoice/pvrecorder-node-demo)<!-- markdown-link-check-disable-line --> provides command-line utilities for recording audio to a file. |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
| (()=>{const stack=new Error().stack;stack&&(globalThis._sentryDebugIds=globalThis._sentryDebugIds||{},globalThis._sentryDebugIds[stack]="b9574cfd-c34f-573a-bb44-bcc9523e9ce0",globalThis._sentryDebugIdIdentifier="sentry-dbid-b9574cfd-c34f-573a-bb44-bcc9523e9ce0");})(); | ||
| /*--------------------------------------------------------------------------------------------- | ||
| * Copyright (c) Microsoft Corporation. All rights reserved. | ||
| *--------------------------------------------------------------------------------------------*/ | ||
| import __module from "module"; | ||
| import __path from "path"; | ||
| import __fs from "fs"; | ||
| const __rootRequire = __module.createRequire(import.meta.url); | ||
| const __appPath = __fs.realpathSync(import.meta.dirname); | ||
| const __clipboardEntrypoint = __path.join(__appPath, "clipboard", "index.js"); | ||
| const __foundryEntrypoint = __path.join(__appPath, "foundry-local-sdk", "index.js"); | ||
| const __pvRecorderEntrypoint = __path.join(__appPath, "pvrecorder", "index.js"); | ||
| const __clipboardRequire = __fs.existsSync(__clipboardEntrypoint) | ||
| ? __module.createRequire(__clipboardEntrypoint) | ||
| : __rootRequire; | ||
| const __foundryRequire = __fs.existsSync(__foundryEntrypoint) | ||
| ? __module.createRequire(__foundryEntrypoint) | ||
| : __rootRequire; | ||
| const __pvRecorderRequire = __fs.existsSync(__pvRecorderEntrypoint) | ||
| ? __module.createRequire(__pvRecorderEntrypoint) | ||
| : __rootRequire; | ||
| const __isVendoredNativeModule = (module) => | ||
| typeof module === "string" && | ||
| (module.startsWith("@teddyzhu/") || module === "foundry-local-sdk" || module === "@picovoice/pvrecorder-node"); | ||
| const require = (module) => { | ||
| let req = __rootRequire; | ||
| if (typeof module === "string" && module.startsWith("@teddyzhu/")) { | ||
| req = __clipboardRequire; | ||
| } | ||
| if (module === "foundry-local-sdk") { | ||
| req = __foundryRequire; | ||
| } | ||
| if (module === "@picovoice/pvrecorder-node") { | ||
| req = __pvRecorderRequire; | ||
| } | ||
| if (typeof module === "string" && (__module.isBuiltin(module) || __isVendoredNativeModule(module))) { | ||
| return req(module); | ||
| } | ||
| const modulePath = __fs.realpathSync(req.resolve(module)); | ||
| const relativePath = __path.relative(__appPath, modulePath); | ||
| if (relativePath.startsWith("..")) { | ||
| throw new Error("Requiring module outside of application is a security concern; module: " + modulePath + ", app: " + __appPath); | ||
| } | ||
| return req(module); | ||
| };import __url from "url"; | ||
| const __filename = __url.fileURLToPath(import.meta.url); | ||
| const __dirname = __path.dirname(__filename); | ||
| import{parentPort as T,workerData as B}from"node:worker_threads";var m=class{initialQueue=[];initialQueueResolvers=Promise.withResolvers();logWriter=null;writePromise=this.initialQueueResolvers.promise;setLogWriter(r){this.logWriter=r;for(let t of this.initialQueue)this.writePromise=this.logWriter.writeLog(t.method,t.message);this.initialQueue=[],this.initialQueueResolvers.resolve()}async flush(){await this.writePromise}async dispose(){await this.flush()}outputPath(){return this.logWriter?.outputPath()}logToLevel(r,t){this.logWriter?this.writePromise=this.logWriter.writeLog(r,t):this.initialQueue.push({method:r,message:t})}info(r){this.logToLevel("info",r)}debug(r){this.logToLevel("debug",r)}warning(r){this.logToLevel("warning",r)}error(r){this.logToLevel("error",r instanceof Error?r.message:r)}log(r){this.error(r)}isDebug(){return!1}shouldLog(r){return!0}notice(r){this.info(r instanceof Error?r.message:r)}startGroup(r,t){this.info(`--- Start of group: ${r} ---`)}endGroup(r){this.info("--- End of group ---")}},u=new m;import{createRequire as j}from"node:module";import*as n from"node:fs/promises";import*as a from"node:path";import{createHash as W}from"node:crypto";import{join as l,basename as oe}from"node:path";import{homedir as h}from"node:os";function A(){return process.env.XDG_CACHE_HOME||l(h(),".cache")}function b(){if(process.platform==="darwin")return l(h(),"Library","Caches","copilot");if(process.platform==="win32"){let e=process.env.LOCALAPPDATA||l(h(),".cache");return l(e,"copilot")}return l(A(),"copilot")}function D(e){if(e.includes("<!DOCTYPE")||e.includes("<html")){let r=Math.min(e.indexOf("<!DOCTYPE")!==-1?e.indexOf("<!DOCTYPE"):1/0,e.indexOf("<html")!==-1?e.indexOf("<html"):1/0),t=e.substring(0,r).trim();return t?`${t} [HTML error page omitted]`:"[HTML error page omitted]"}return e}function y(e){let r;if(e instanceof Error)r=String(e);else if(typeof e=="object"&&e!==null)try{r=JSON.stringify(e)??"[object]"}catch{return"[object with circular reference]"}else r=String(e);return D(r)}var H=1,I=".complete";var w={"win32-x64":"win-x64","win32-arm64":"win-arm64","linux-x64":"linux-x64","darwin-arm64":"osx-arm64"};function S(){return typeof __foundryRequire<"u"&&__foundryRequire||j(import.meta.url)}var f;function U(){if(f)return f;try{let e=S()("foundry-local-sdk/script/install-utils.cjs");if(typeof e.runInstall!="function")throw new Error(`Expected exports {runInstall: function}, got: ${JSON.stringify(Object.fromEntries(Object.entries(e).map(([r,t])=>[r,typeof t])))}`);return f=e,f}catch(e){throw new Error(`Failed to load foundry-local-sdk/script/install-utils.cjs: ${y(e)}. The upstream foundry-local-sdk installer may have changed shape \u2014 re-run the audit checklist in src/cli/voice/foundry/installer/nativeLoader.ts and update accordingly.`)}}var p;function J(){if(p)return p;try{let e=S()("foundry-local-sdk/deps_versions.json");if(typeof e["foundry-local-core"]?.nuget!="string"||typeof e.onnxruntime?.version!="string"||typeof e["onnxruntime-genai"]?.version!="string")throw new Error('deps_versions.json is missing one of the expected version keys: ["foundry-local-core"].nuget, .onnxruntime.version, ["onnxruntime-genai"].version');return p=e,p}catch(e){throw new Error(`Failed to load foundry-local-sdk/deps_versions.json: ${y(e)}. The upstream foundry-local-sdk installer may have changed shape \u2014 re-run the audit checklist in src/cli/voice/foundry/installer/nativeLoader.ts and update accordingly.`)}}function O(e=process.platform){let r=J();return[{name:"Microsoft.AI.Foundry.Local.Core",version:r["foundry-local-core"].nuget},{name:e==="linux"?"Microsoft.ML.OnnxRuntime.Gpu.Linux":"Microsoft.ML.OnnxRuntime.Foundry",version:r.onnxruntime.version},{name:"Microsoft.ML.OnnxRuntimeGenAI.Foundry",version:r["onnxruntime-genai"].version}]}function C(e){return e==="win32"?".dll":e==="darwin"?".dylib":".so"}function V(e,r){return a.join(e,`Microsoft.AI.Foundry.Local.Core${C(r)}`)}function q(e){let r=C(e),t=e==="win32"?"":"lib";return[`Microsoft.AI.Foundry.Local.Core${r}`,`${t}onnxruntime${r}`,`${t}onnxruntime-genai${r}`]}function G(e,r=process.platform,t=process.arch){let o=w[`${r}-${t}`];if(!o)throw new Error(`Voice mode not supported on ${r}-${t}`);let i=e??process.env.COPILOT_CACHE_HOME??b(),s=O(r),c=W("sha256").update(JSON.stringify({schema:H,artifacts:s})).digest("hex").slice(0,12);return a.join(i,"foundry",c,o)}async function _(e={}){let r=e.platform??process.platform,t=e.arch??process.arch,o=`${r}-${t}`;if(!w[o])throw new Error(`Voice mode is not supported on ${o}. Supported platforms: ${Object.keys(w).join(", ")}.`);let s=G(e.cacheRoot,r,t),c=V(s,r),d=q(r);return await M(s,d)?{corePath:c}:(e.onDownloadStart?.(),await Y(s,r,d,e.runInstall),{corePath:c})}async function M(e,r){return await v(a.join(e,I))?(await Promise.all(r.map(o=>v(a.join(e,o))))).every(Boolean):!1}async function v(e){try{return await n.access(e),!0}catch{return!1}}async function Y(e,r,t,o){let i=a.dirname(e);await n.mkdir(i,{recursive:!0});let s=a.join(i,`.tmp-${a.basename(e)}-${process.pid}-${Date.now()}`);await n.mkdir(s,{recursive:!0});try{let c=o??U().runInstall,d=O(r);await z(()=>c(d,{binDir:s}));for(let P of t)if(!await v(a.join(s,P)))throw new Error(`Foundry runtime download finished but required file is missing: ${P}. RID for ${r} may not be supported by the published packages.`);await n.writeFile(a.join(s,I),""),await K(s,e,t)}catch(c){throw await n.rm(s,{recursive:!0,force:!0}).catch(()=>{}),c}}async function K(e,r,t){try{await n.rename(e,r)}catch(o){let i=o.code;if(i==="ENOTEMPTY"||i==="EEXIST"||i==="EPERM"){if(await M(r,t)){await n.rm(e,{recursive:!0,force:!0}).catch(()=>{});return}await n.rm(r,{recursive:!0,force:!0}),await n.rename(e,r);return}throw o}}async function z(e){let r=process.stdout.write.bind(process.stdout),t=process.stderr.write.bind(process.stderr);process.stdout.write=(()=>!0),process.stderr.write=(()=>!0);try{return await e()}finally{process.stdout.write=r,process.stderr.write=t}}var E=class extends Error{constructor(t,o,i){super(t,i);this.code=o;this.name="VoiceBackendError"}};function N(e){return e instanceof E?{message:e.message,code:e.code}:e instanceof Error?{message:e.message}:{message:String(e)}}function R(e){return e instanceof Error?e:new Error(String(e))}var Q=16;function x(e){return $(e,new WeakSet,0)}function $(e,r,t){if(t>=Q)return"<cause chain truncated>";if(typeof e=="object"&&e!==null){if(r.has(e))return"<cyclic cause>";r.add(e)}if(!(e instanceof Error))return String(e);let o=e.stack??`${e.name}: ${e.message}`;if(e.cause===void 0)return o;let i=$(e.cause,r,t+1);return`${o} | ||
| Caused by: ${i}`}var k=16*1024,L=class{constructor(r){this.port=r}writeLog(r,t){let o={kind:"log",level:r,message:X(t)};try{this.port.postMessage(o)}catch{}return Promise.resolve()}outputPath(){return"<voice-worker>"}};function F(e,r=u){r.setLogWriter(new L(e))}function X(e){return e.length<=k?e:`${e.slice(0,k)}\u2026 [truncated, ${e.length-k} more chars]`}if(!T)throw new Error("voice-installer.worker.js must be loaded as a worker thread.");var g=T;F(g);var Z=B??{};async function ee(){try{let r={kind:"ok",location:await _({cacheRoot:Z.cacheRoot,onDownloadStart:()=>{let t={kind:"download-started"};g.postMessage(t)}})};g.postMessage(r)}catch(e){let r=R(e);u.error(`[voice-installer worker] install failed: ${x(r)}`);let t={kind:"error",error:N(r)};g.postMessage(t)}finally{setImmediate(()=>process.exit(0))}}ee().catch(e=>{u.error(`[voice-installer worker] fatal: ${x(e)}`),process.exit(1)}); | ||
| //# sourceMappingURL=voice-installer.worker.js.map |
Sorry, the diff of this file is too big to display
| (()=>{const stack=new Error().stack;stack&&(globalThis._sentryDebugIds=globalThis._sentryDebugIds||{},globalThis._sentryDebugIds[stack]="148ffc1d-a79c-5ba4-881a-37f8b4d1d4f0",globalThis._sentryDebugIdIdentifier="sentry-dbid-148ffc1d-a79c-5ba4-881a-37f8b4d1d4f0");})(); | ||
| /*--------------------------------------------------------------------------------------------- | ||
| * Copyright (c) Microsoft Corporation. All rights reserved. | ||
| *--------------------------------------------------------------------------------------------*/ | ||
| import __module from "module"; | ||
| import __path from "path"; | ||
| import __fs from "fs"; | ||
| const __rootRequire = __module.createRequire(import.meta.url); | ||
| const __appPath = __fs.realpathSync(import.meta.dirname); | ||
| const __clipboardEntrypoint = __path.join(__appPath, "clipboard", "index.js"); | ||
| const __foundryEntrypoint = __path.join(__appPath, "foundry-local-sdk", "index.js"); | ||
| const __pvRecorderEntrypoint = __path.join(__appPath, "pvrecorder", "index.js"); | ||
| const __clipboardRequire = __fs.existsSync(__clipboardEntrypoint) | ||
| ? __module.createRequire(__clipboardEntrypoint) | ||
| : __rootRequire; | ||
| const __foundryRequire = __fs.existsSync(__foundryEntrypoint) | ||
| ? __module.createRequire(__foundryEntrypoint) | ||
| : __rootRequire; | ||
| const __pvRecorderRequire = __fs.existsSync(__pvRecorderEntrypoint) | ||
| ? __module.createRequire(__pvRecorderEntrypoint) | ||
| : __rootRequire; | ||
| const __isVendoredNativeModule = (module) => | ||
| typeof module === "string" && | ||
| (module.startsWith("@teddyzhu/") || module === "foundry-local-sdk" || module === "@picovoice/pvrecorder-node"); | ||
| const require = (module) => { | ||
| let req = __rootRequire; | ||
| if (typeof module === "string" && module.startsWith("@teddyzhu/")) { | ||
| req = __clipboardRequire; | ||
| } | ||
| if (module === "foundry-local-sdk") { | ||
| req = __foundryRequire; | ||
| } | ||
| if (module === "@picovoice/pvrecorder-node") { | ||
| req = __pvRecorderRequire; | ||
| } | ||
| if (typeof module === "string" && (__module.isBuiltin(module) || __isVendoredNativeModule(module))) { | ||
| return req(module); | ||
| } | ||
| const modulePath = __fs.realpathSync(req.resolve(module)); | ||
| const relativePath = __path.relative(__appPath, modulePath); | ||
| if (relativePath.startsWith("..")) { | ||
| throw new Error("Requiring module outside of application is a security concern; module: " + modulePath + ", app: " + __appPath); | ||
| } | ||
| return req(module); | ||
| };import __url from "url"; | ||
| const __filename = __url.fileURLToPath(import.meta.url); | ||
| const __dirname = __path.dirname(__filename); | ||
| var a=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(r,v)=>(typeof require<"u"?require:r)[v]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var u=(e,r)=>()=>(r||e((r={exports:{}}).exports,r),r.exports);var i=u(t=>{"use strict";Object.defineProperty(t,"__esModule",{value:!0});t.getWorkerPipeName=P;function P(e){return"".concat(e,"-worker")}});var _=u(p=>{Object.defineProperty(p,"__esModule",{value:!0});var o=a("worker_threads"),s=a("net"),k=i(),c=o.workerData.conoutPipeName,n=new s.Socket;n.setEncoding("utf8");n.connect(c,function(){var e=(0,s.createServer)(function(r){n.pipe(r)});if(e.listen((0,k.getWorkerPipeName)(c)),!o.parentPort)throw new Error("worker_threads parentPort is null");o.parentPort.postMessage(1)})});export default _(); | ||
| //# sourceMappingURL=conoutSocketWorker.js.map |
+10
-6
| { | ||
| "name": "@github/copilot-linuxmusl-arm64", | ||
| "version": "1.0.64-0", | ||
| "description": "GitHub Copilot CLI executable for linuxmusl-arm64", | ||
| "version": "1.0.64-1", | ||
| "description": "GitHub Copilot CLI for linuxmusl-arm64", | ||
| "license": "SEE LICENSE IN LICENSE.md", | ||
| "type": "module", | ||
| "repository": { | ||
@@ -20,9 +21,12 @@ "type": "git", | ||
| ], | ||
| "files": [ | ||
| "copilot" | ||
| ], | ||
| "bin": { | ||
| "copilot-linuxmusl-arm64": "copilot" | ||
| }, | ||
| "exports": "./copilot", | ||
| "exports": { | ||
| ".": "./copilot", | ||
| "./sdk": { | ||
| "types": "./sdk/index.d.ts", | ||
| "import": "./sdk/index.js" | ||
| } | ||
| }, | ||
| "libc": [ | ||
@@ -29,0 +33,0 @@ "musl" |
+5
-1
| # @github/copilot-linuxmusl-arm64 | ||
| [GitHub Copilot CLI](https://github.com/github/copilot-cli) executable for `linuxmusl-arm64`. | ||
| [GitHub Copilot CLI](https://github.com/github/copilot-cli) for `linuxmusl-arm64`. | ||
| This package contains both the native executable and the JavaScript bundle | ||
| with platform-specific native addons. It is installed automatically as an | ||
| optional dependency of [`@github/copilot`](https://www.npmjs.com/package/@github/copilot). |
Install scripts
Supply chain riskInstall scripts are run when the package is installed or built. Malicious packages often use scripts that run automatically to execute payloads or fetch additional code.
Found 2 instances in 1 package
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 2 instances in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 24 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 2 instances in 1 package
Mixed license
LicensePackage contains multiple licenses.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Empty package
Supply chain riskPackage does not contain any code. It may be removed, is name squatting, or the result of a faulty package publish.
Found 1 instance in 1 package
250882476
155.69%198
4850%221925
Infinity%7
133.33%Yes
NaN2
100%6
500%2
Infinity%100
Infinity%36
1700%