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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions command-snapshot.json
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,14 @@
],
"plugin": "@salesforce/plugin-packaging"
},
{
"alias": [],
"command": "package:trust:link:list",
"flagAliases": ["apiversion", "targetusername", "u"],
"flagChars": ["o", "s"],
"flags": ["api-version", "flags-dir", "json", "loglevel", "status", "target-org"],
"plugin": "@salesforce/plugin-packaging"
},
{
"alias": ["force:package:uninstall"],
"command": "package:uninstall",
Expand Down
33 changes: 33 additions & 0 deletions messages/package_trust_link_list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# summary

List Public Secure (VerifiedDev) trust link requests for this verified org.

# description

Run this command against a verified packaging org (PBO). It lists inbound trust requests from authoring orgs (1GP namespace orgs or 2GP Dev Hubs).

Results include the request ID, requesting user, authoring org ID, status, and request date. Use --status to filter. Status "approved" maps to an Accepted trust.

Authoring org name and that org's packages are not returned by the Tooling API for this entity; use the request ID with approve, deny, or revoke.

# examples

- List all inbound Public Secure trust link requests in the target verified org:

<%= config.bin %> <%= command.id %> --target-org pbo@example.com

- List only pending requests:

<%= config.bin %> <%= command.id %> --target-org pbo@example.com --status pending

- List accepted (approved) links as JSON:

<%= config.bin %> <%= command.id %> --target-org pbo@example.com --status approved --json

# flags.status.summary

Filter results by request status: pending, approved, declined, or revoked.

# flags.status.description

"approved" selects Accepted records. Failed requests are included only when this flag is omitted.
56 changes: 56 additions & 0 deletions schemas/package-trust-link-list.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "#/definitions/PackageTrustLinkListCommandResult",
"definitions": {
"PackageTrustLinkListCommandResult": {
"type": "array",
"items": {
"$ref": "#/definitions/PackageTrustLinkRecord"
}
},
"PackageTrustLinkRecord": {
"type": "object",
"properties": {
"Id": {
"type": "string"
},
"AuthoringOrg": {
"type": "string"
},
"VerifiedOrg": {
"type": "string"
},
"Status": {
"$ref": "#/definitions/PackageTrustLinkStatus"
},
"RequestedBy": {
"type": ["string", "null"]
},
"CreatedDate": {
"type": "string"
},
"EstablishedDate": {
"type": ["string", "null"]
},
"RevokedDate": {
"type": ["string", "null"]
}
},
"required": [
"Id",
"AuthoringOrg",
"VerifiedOrg",
"Status",
"RequestedBy",
"CreatedDate",
"EstablishedDate",
"RevokedDate"
],
"additionalProperties": false
},
"PackageTrustLinkStatus": {
"type": "string",
"enum": ["Pending", "Accepted", "Declined", "Revoked", "Failed"]
}
}
}
76 changes: 76 additions & 0 deletions src/commands/package/trust/link/list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright 2026, Salesforce, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/

import {
Flags,
loglevel,
orgApiVersionFlagWithDeprecations,
requiredOrgFlagWithDeprecations,
SfCommand,
} from '@salesforce/sf-plugins-core';
import { Messages } from '@salesforce/core/messages';
import { PackageTrustLink, PackageTrustLinkListStatusFilter, PackageTrustLinkRecord } from '@salesforce/packaging';

Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
const messages = Messages.loadMessages('@salesforce/plugin-packaging', 'package_trust_link_list');

const STATUS_OPTIONS: PackageTrustLinkListStatusFilter[] = ['pending', 'approved', 'declined', 'revoked'];

export type PackageTrustLinkListCommandResult = PackageTrustLinkRecord[];

export class PackageTrustLinkListCommand extends SfCommand<PackageTrustLinkListCommandResult> {
public static readonly hidden = true;
public static state = 'beta';
public static readonly summary = messages.getMessage('summary');
public static readonly description = messages.getMessage('description');
public static readonly examples = messages.getMessages('examples');
public static readonly flags = {
loglevel,
'target-org': requiredOrgFlagWithDeprecations,
'api-version': orgApiVersionFlagWithDeprecations,
status: Flags.custom<PackageTrustLinkListStatusFilter>({
options: STATUS_OPTIONS,
})({
char: 's',
summary: messages.getMessage('flags.status.summary'),
description: messages.getMessage('flags.status.description'),
}),
};

public async run(): Promise<PackageTrustLinkListCommandResult> {
const { flags } = await this.parse(PackageTrustLinkListCommand);
const connection = flags['target-org'].getConnection(flags['api-version']);
const results = await PackageTrustLink.list(connection, flags.status);

if (results.length === 0) {
this.warn('No results found');
} else {
this.table({
data: results.map((r) => ({
Id: r.Id,
'Requested By': r.RequestedBy ?? '',
'Authoring Org': r.AuthoringOrg,
Status: r.Status,
'Request Date': r.CreatedDate,
})),
title: `Trust Link Requests [${results.length}]`,
overflow: 'wrap',
});
}

return results;
}
}
82 changes: 82 additions & 0 deletions test/commands/package/packageTrustLinkList.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright 2026, Salesforce, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
import { Config } from '@oclif/core';
import { TestContext, MockTestOrgData } from '@salesforce/core/testSetup';
import * as sinon from 'sinon';
import { expect } from 'chai';
import { stubSfCommandUx } from '@salesforce/sf-plugins-core';
import { PackageTrustLink, PackageTrustLinkRecord } from '@salesforce/packaging';
import { PackageTrustLinkListCommand } from '../../../src/commands/package/trust/link/list.js';

const trustLinkListSuccess: PackageTrustLinkRecord[] = [
{
Id: '2vt000000000001AAA',
AuthoringOrg: '00D000000000002',
VerifiedOrg: '00D000000000001',
Status: 'Pending',
RequestedBy: 'Ada Lovelace',
CreatedDate: '2026-08-24T00:00:00.000Z',
EstablishedDate: null,
RevokedDate: null,
},
];

describe('package:trust:link:list - tests', () => {
const $$ = new TestContext();
const testOrg = new MockTestOrgData();
let sfCommandStubs: ReturnType<typeof stubSfCommandUx>;
let listStub: sinon.SinonStub;
const config = new Config({ root: import.meta.url });

beforeEach(async () => {
await $$.stubAuths(testOrg);
await config.load();
sfCommandStubs = stubSfCommandUx($$.SANDBOX);
listStub = $$.SANDBOX.stub(PackageTrustLink, 'list').resolves(trustLinkListSuccess);
});

afterEach(() => {
$$.restore();
});

it('lists inbound Public Secure trust link requests', async () => {
const cmd = new PackageTrustLinkListCommand(['-o', testOrg.username, '--api-version', '68.0'], config);
const result = await cmd.run();

expect(result).to.deep.equal(trustLinkListSuccess);
expect(listStub.calledOnce).to.equal(true);
expect(listStub.firstCall.args[1]).to.equal(undefined);
expect(sfCommandStubs.table.called).to.equal(true);
});

it('passes the status filter to PackageTrustLink.list', async () => {
const cmd = new PackageTrustLinkListCommand(
['-o', testOrg.username, '--api-version', '68.0', '--status', 'pending'],
config
);
await cmd.run();
expect(listStub.calledOnce).to.equal(true);
expect(listStub.firstCall.args[1]).to.equal('pending');
});

it('warns when there are no results', async () => {
listStub.resolves([]);
const cmd = new PackageTrustLinkListCommand(['-o', testOrg.username, '--api-version', '68.0'], config);
const result = await cmd.run();
expect(result).to.deep.equal([]);
expect(sfCommandStubs.warn.called).to.equal(true);
});
});
Loading