Skip to content

Commit d9f40aa

Browse files
author
colinmcneil
committed
Prepare secret storage host binary for 0.2.7 release
1 parent 69e224e commit d9f40aa

File tree

7 files changed

+210
-86
lines changed

7 files changed

+210
-86
lines changed

src/extension/Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
IMAGE?=docker/labs-ai-tools-for-devs
2-
TAG?=0.1.4
2+
TAG?=0.2.7
33

44
BUILDER=buildx-multi-arch
55

@@ -14,7 +14,7 @@ cross:
1414
cd host-binary && $(MAKE) cross
1515

1616
build-extension: cross ## Build service image to be deployed as a desktop extension
17-
docker build --platform=linux/amd64,linux/arm64,darwin/amd64,darwin/arm64,windows/amd64,windows/arm64 --tag=$(IMAGE):$(TAG) .
17+
docker buildx build --load --platform=linux/amd64,linux/arm64,darwin/amd64,darwin/arm64,windows/amd64,windows/arm64 --tag=$(IMAGE):$(TAG) .
1818

1919
install-extension: build-extension ## Install the extension
2020
docker extension install $(IMAGE):$(TAG)

src/extension/host-binary/cmd/main.go

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ package main
22

33
import (
44
"context"
5+
"encoding/json"
56
"fmt"
6-
"github.com/docker/labs-ai-tools-for-devs/pkg/client"
7-
secretsapi "github.com/docker/labs-ai-tools-for-devs/pkg/generated/go/client/secrets"
8-
"github.com/docker/labs-ai-tools-for-devs/pkg/paths"
97
"os"
108
"os/signal"
119
"syscall"
12-
10+
secretsapi "github.com/docker/labs-ai-tools-for-devs/pkg/generated/go/client/secrets"
11+
"github.com/docker/labs-ai-tools-for-devs/pkg/client"
12+
"github.com/docker/labs-ai-tools-for-devs/pkg/paths"
1313
"github.com/spf13/cobra"
1414
)
1515

@@ -18,6 +18,7 @@ func main() {
1818
defer closeFunc()
1919
paths.Init(paths.OnHost)
2020
cmd := AddSecret(ctx)
21+
cmd.AddCommand(ListSecrets(ctx))
2122
if err := cmd.Execute(); err != nil {
2223
fmt.Println(err)
2324
os.Exit(1)
@@ -66,6 +67,18 @@ func AddSecret(ctx context.Context) *cobra.Command {
6667
return cmd
6768
}
6869

70+
func ListSecrets(ctx context.Context) *cobra.Command {
71+
cmd := &cobra.Command{
72+
Use: "list",
73+
Short: "List all secrets",
74+
Args: cobra.NoArgs,
75+
RunE: func(*cobra.Command, []string) error {
76+
return runListSecrets(ctx)
77+
},
78+
}
79+
return cmd
80+
}
81+
6982
const mcpPolicyName = "MCP"
7083

7184
func runAddSecret(ctx context.Context, opts addOptions) error {
@@ -79,6 +92,18 @@ func runAddSecret(ctx context.Context, opts addOptions) error {
7992
return c.SetSecret(ctx, secretsapi.Secret{Name: opts.Name, Value: opts.Value, Policies: []string{mcpPolicyName}})
8093
}
8194

95+
func runListSecrets(ctx context.Context) error {
96+
c, err := newApiClient()
97+
if err != nil {
98+
return err
99+
}
100+
secrets, err := c.ListSecrets(ctx)
101+
if err != nil {
102+
return err
103+
}
104+
return json.NewEncoder(os.Stdout).Encode(secrets)
105+
}
106+
82107
func assertMcpPolicyExists(ctx context.Context, apiClient client.ApiClient) error {
83108
return apiClient.SetPolicy(ctx, secretsapi.Policy{Name: mcpPolicyName, Images: []string{"*"}})
84109
}

src/extension/metadata.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,17 @@
1818
{
1919
"darwin": [
2020
{
21-
"path": "/darwin/api-client"
21+
"path": "/darwin/host-binary"
2222
}
2323
],
2424
"linux": [
2525
{
26-
"path": "/linux/api-client"
26+
"path": "/linux/host-binary"
2727
}
2828
],
2929
"windows": [
3030
{
31-
"path": "/windows/api-client.exe"
31+
"path": "/windows/host-binary.exe"
3232
}
3333
]
3434
}

src/extension/ui/src/Secrets.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// From secrets.yaml
2+
3+
import { v1 } from "@docker/extension-api-client-types";
4+
import { CatalogItemWithName } from "./components/PromptCard";
5+
6+
namespace Secrets {
7+
export type Secret = {
8+
name: string;
9+
value: string;
10+
policies: string[];
11+
}
12+
13+
export type StoredSecret = {
14+
name: string;
15+
policies: string[];
16+
}
17+
18+
export type Policy = {
19+
name: string;
20+
images: string[];
21+
}
22+
23+
export async function getSecrets(client: v1.DockerDesktopClient): Promise<Secret[]> {
24+
const response = await client.extension.host?.cli.exec('host-binary', ['list']);
25+
return JSON.parse(response?.stdout || '[]');
26+
}
27+
28+
export async function addSecret(client: v1.DockerDesktopClient, secret: Secret): Promise<void> {
29+
const response = await client.extension.host?.cli.exec('host-binary', ['add', secret.name, secret.value]);
30+
console.log(response);
31+
}
32+
33+
// Get all relevant secrets for a given set of catalog items
34+
export function getAllSecretNames(catalogItems: CatalogItemWithName[]): string[] {
35+
return catalogItems.map((item) => item.secrets || []).flat().map((secret) => secret.name);
36+
}
37+
38+
// Whether or not each secret has been assigned for a given catalog item
39+
export function getAssignedSecrets(catalogItem: CatalogItemWithName, secrets: Secret[]): { name: string, assigned: boolean }[] {
40+
return catalogItem.secrets?.map((secret) => ({ name: secret.name, assigned: secrets.some((s) => s.name === secret.name) })) || [];
41+
}
42+
}
43+
44+
export default Secrets;

src/extension/ui/src/components/CatalogGrid.tsx

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { getRegistry } from '../Registry';
99
import { FolderOpenRounded, Search, Settings } from '@mui/icons-material';
1010
import { tryRunImageSync } from '../FileWatcher';
1111
import { CATALOG_URL, POLL_INTERVAL } from '../Constants';
12+
import { SecretList } from './SecretList';
13+
import Secrets from '../Secrets';
1214

1315
interface CatalogGridProps {
1416
registryItems: { [key: string]: { ref: string } };
@@ -38,6 +40,7 @@ export const CatalogGrid: React.FC<CatalogGridProps> = ({
3840
const [showReloadModal, setShowReloadModal] = useState<boolean>(false);
3941
const [search, setSearch] = useState<string>('');
4042
const [tab, setTab] = useState<number>(0);
43+
const [secrets, setSecrets] = useState<Secrets.Secret[]>([]);
4144

4245
const filteredCatalogItems = filterCatalog(catalogItems, registryItems, showRegistered, showUnregistered, search);
4346

@@ -65,6 +68,12 @@ export const CatalogGrid: React.FC<CatalogGridProps> = ({
6568
}
6669
}
6770

71+
const loadSecrets = async () => {
72+
const response = await Secrets.getSecrets(client);
73+
console.log(response);
74+
setSecrets(response);
75+
}
76+
6877
const registerCatalogItem = async (item: CatalogItemWithName) => {
6978
try {
7079
const currentRegistry = await getRegistry(client);
@@ -107,8 +116,10 @@ export const CatalogGrid: React.FC<CatalogGridProps> = ({
107116

108117
useEffect(() => {
109118
loadCatalog(false);
119+
loadSecrets();
110120
const interval = setInterval(() => {
111121
loadCatalog(false);
122+
loadSecrets();
112123
}, POLL_INTERVAL);
113124
return () => {
114125
clearInterval(interval);
@@ -144,10 +155,13 @@ export const CatalogGrid: React.FC<CatalogGridProps> = ({
144155
</Alert>}
145156
<Tabs value={tab} onChange={(e, v) => setTab(v)} sx={{ mb: 0, mt: 1 }}>
146157
<Tooltip title="These are all of the tiles you have available across the catalog.">
147-
<Tab sx={{ fontSize: '1.5em' }} label="Available" />
158+
<Tab sx={{ fontSize: '1.5em' }} label="Tool Catalog" />
148159
</Tooltip>
149160
<Tooltip title="These are tiles which you have allowed MCP clients to use.">
150-
<Tab sx={{ fontSize: '1.5em' }} label="Allowed" />
161+
<Tab sx={{ fontSize: '1.5em' }} label="Your Tools" />
162+
</Tooltip>
163+
<Tooltip title="These are secrets which you have set for your MCP clients.">
164+
<Tab sx={{ fontSize: '1.5em' }} label="Secrets" />
151165
</Tooltip>
152166
</Tabs>
153167
<FormGroup sx={{ width: '100%', mt: 0 }}>
@@ -203,6 +217,7 @@ export const CatalogGrid: React.FC<CatalogGridProps> = ({
203217
</Grid2>
204218
))}
205219
</Grid2>}
220+
{tab === 2 && <SecretList secrets={secrets} />}
206221
</Stack >
207222
);
208223
};

0 commit comments

Comments
 (0)