diff --git a/README.md b/README.md
index 1c4bc79..bbd6fb8 100644
--- a/README.md
+++ b/README.md
@@ -161,6 +161,22 @@ Current gaps:
+
+Identity
+
+AWS only, through the generic identity service category.
+
+- List and inspect IAM users.
+- Create and delete IAM users.
+- IAM user paths are supported during creation.
+
+Current gaps:
+
+- Roles, groups, policies, access keys, and other advanced IAM workflows are not exposed yet.
+- No Azure or GCP identity adapter yet.
+
+
+
Serverless
diff --git a/packages/api/package.json b/packages/api/package.json
index 4c60859..5cc4d8a 100644
--- a/packages/api/package.json
+++ b/packages/api/package.json
@@ -12,6 +12,7 @@
"dependencies": {
"@aws-sdk/client-ec2": "^3.1076.0",
"@aws-sdk/client-eks": "^3.1076.0",
+ "@aws-sdk/client-iam": "^3.1076.0",
"@aws-sdk/client-lambda": "^3.1076.0",
"@aws-sdk/client-rds": "^3.1076.0",
"@aws-sdk/client-s3": "^3.1076.0",
diff --git a/packages/api/src/adapter-aws/AwsIamAdapter.test.ts b/packages/api/src/adapter-aws/AwsIamAdapter.test.ts
new file mode 100644
index 0000000..c4d5a99
--- /dev/null
+++ b/packages/api/src/adapter-aws/AwsIamAdapter.test.ts
@@ -0,0 +1,127 @@
+import {describe, expect, test} from 'bun:test'
+import {
+ CreateUserCommand,
+ DeleteUserCommand,
+ GetUserCommand,
+ ListUsersCommand,
+ type IAMClient,
+} from '@aws-sdk/client-iam'
+import {AwsIamAdapter} from './AwsIamAdapter'
+
+function fakeClient(handler: (command: unknown) => unknown): IAMClient {
+ return {
+ send: async (command: unknown) => handler(command),
+ } as unknown as IAMClient
+}
+
+const alice = {
+ UserName: 'alice',
+ UserId: 'AIDAALICE',
+ Arn: 'arn:aws:iam::000000000000:user/team/alice',
+ Path: '/team/',
+ CreateDate: new Date('2026-01-02T03:04:05.000Z'),
+}
+
+describe('AwsIamAdapter', () => {
+ test('lists every page and maps IAM users to normalized resources', async () => {
+ const adapter = new AwsIamAdapter(fakeClient((command) => {
+ if (!(command instanceof ListUsersCommand)) throw new Error('Unexpected command')
+ if (!command.input.Marker) return {Users: [alice], IsTruncated: true, Marker: 'next'}
+ return {Users: [{...alice, UserName: 'bob', UserId: 'AIDABOB'}], IsTruncated: false}
+ }))
+
+ const result = await adapter.list()
+
+ expect(result).toHaveLength(2)
+ expect(result[0]).toMatchObject({
+ id: 'alice',
+ name: 'alice',
+ cloud: 'aws',
+ service: 'identity',
+ type: 'iam-user',
+ region: null,
+ createdAt: '2026-01-02T03:04:05.000Z',
+ })
+ expect(result[0].metadata).toMatchObject({
+ identityService: 'iam',
+ userId: 'AIDAALICE',
+ path: '/team/',
+ })
+ })
+
+ test('filters users by search term', async () => {
+ const adapter = new AwsIamAdapter(fakeClient(() => ({
+ Users: [alice, {...alice, UserName: 'bob'}],
+ IsTruncated: false,
+ })))
+
+ const result = await adapter.list({search: 'ALI'})
+
+ expect(result.map((resource) => resource.name)).toEqual(['alice'])
+ })
+
+ test('gets and maps one IAM user', async () => {
+ const adapter = new AwsIamAdapter(fakeClient((command) => {
+ expect(command).toBeInstanceOf(GetUserCommand)
+ return {User: alice}
+ }))
+
+ const result = await adapter.get('alice')
+
+ expect(result?.id).toBe('alice')
+ expect(result?.metadata.arn).toBe(alice.Arn)
+ })
+
+ test('returns null when IAM reports a missing user', async () => {
+ const adapter = new AwsIamAdapter(fakeClient(() => {
+ throw Object.assign(new Error('missing'), {name: 'NoSuchEntityException'})
+ }))
+
+ await expect(adapter.get('missing')).resolves.toBeNull()
+ })
+
+ test('creates an IAM user with an optional path', async () => {
+ const adapter = new AwsIamAdapter(fakeClient((command) => {
+ expect(command).toBeInstanceOf(CreateUserCommand)
+ expect((command as CreateUserCommand).input).toEqual({UserName: 'alice', Path: '/team/'})
+ return {User: alice}
+ }))
+
+ const result = await adapter.create({values: {userName: 'alice', path: '/team/'}})
+
+ expect(result.id).toBe('alice')
+ })
+
+ test('rejects invalid user names before calling IAM', async () => {
+ let called = false
+ const adapter = new AwsIamAdapter(fakeClient(() => {
+ called = true
+ return {}
+ }))
+
+ await expect(adapter.create({values: {userName: 'not valid'}})).rejects.toThrow('Use a valid IAM user name')
+ await expect(adapter.create({values: {userName: 'alice', path: 'team'}})).rejects.toThrow('Use a valid IAM path')
+ expect(called).toBeFalse()
+ })
+
+ test('deletes the requested IAM user', async () => {
+ const adapter = new AwsIamAdapter(fakeClient((command) => {
+ expect(command).toBeInstanceOf(DeleteUserCommand)
+ expect((command as DeleteUserCommand).input.UserName).toBe('alice')
+ return {}
+ }))
+
+ await adapter.delete('alice')
+ })
+
+ test('returns the AWS IAM schema', () => {
+ const adapter = new AwsIamAdapter(fakeClient(() => ({})))
+
+ expect(adapter.schema()).toMatchObject({
+ cloud: 'aws',
+ service: 'identity',
+ displayName: 'AWS IAM users',
+ actions: ['list', 'create', 'delete', 'inspect'],
+ })
+ })
+})
diff --git a/packages/api/src/adapter-aws/AwsIamAdapter.ts b/packages/api/src/adapter-aws/AwsIamAdapter.ts
new file mode 100644
index 0000000..cbc7689
--- /dev/null
+++ b/packages/api/src/adapter-aws/AwsIamAdapter.ts
@@ -0,0 +1,106 @@
+import {
+ CreateUserCommand,
+ DeleteUserCommand,
+ GetUserCommand,
+ ListUsersCommand,
+ type IAMClient,
+ type User,
+} from '@aws-sdk/client-iam'
+import {iam as defaultIam} from '../aws'
+import {awsIamSchema} from '../cloud-spi/iamSchema'
+import type {CloudResource, CloudServiceAdapter, CreateResourceInput, ResourceQuery, ServiceSchema} from '../cloud-spi/types'
+
+export class AwsIamAdapter implements CloudServiceAdapter {
+ readonly cloud = 'aws' as const
+ readonly service = 'identity' as const
+
+ constructor(private readonly iam: IAMClient = defaultIam) {}
+
+ schema(): ServiceSchema {
+ return awsIamSchema()
+ }
+
+ async list(query: ResourceQuery = {}): Promise {
+ const users: User[] = []
+ let marker: string | undefined
+
+ do {
+ const res = await this.iam.send(new ListUsersCommand({Marker: marker}))
+ users.push(...(res.Users ?? []))
+ marker = res.IsTruncated ? res.Marker : undefined
+ } while (marker)
+
+ return filterBySearch(users.map(toResource), query.search)
+ }
+
+ async get(id: string): Promise {
+ try {
+ const res = await this.iam.send(new GetUserCommand({UserName: id}))
+ return res.User ? toResource(res.User) : null
+ } catch (error) {
+ if (isNotFound(error)) return null
+ throw error
+ }
+ }
+
+ async create(input: CreateResourceInput): Promise {
+ const userName = stringValue(input.values.userName)
+ const path = stringValue(input.values.path)
+ if (!userName) throw new Error('userName is required')
+ if (!/^[A-Za-z0-9_+=,.@-]{1,64}$/.test(userName)) {
+ throw new Error('Use a valid IAM user name: 1-64 letters, numbers, and +=,.@_- characters.')
+ }
+ if (path && (path.length > 512 || !/^\/(?:[!-~]+\/)?$/.test(path))) {
+ throw new Error('Use a valid IAM path: begin and end with / and use only printable ASCII characters.')
+ }
+
+ const res = await this.iam.send(new CreateUserCommand({
+ UserName: userName,
+ Path: path || undefined,
+ }))
+ if (!res.User) throw new Error('AWS IAM did not return the created user')
+ return toResource(res.User)
+ }
+
+ async delete(id: string): Promise {
+ await this.iam.send(new DeleteUserCommand({UserName: id}))
+ }
+}
+
+function toResource(user: User): CloudResource {
+ const userName = user.UserName ?? ''
+ return {
+ id: userName,
+ name: userName,
+ cloud: 'aws',
+ service: 'identity',
+ type: 'iam-user',
+ region: null,
+ createdAt: user.CreateDate?.toISOString() ?? null,
+ metadata: {
+ provider: 'aws',
+ identityService: 'iam',
+ userId: user.UserId,
+ arn: user.Arn,
+ path: user.Path,
+ permissionsBoundary: user.PermissionsBoundary,
+ tags: user.Tags,
+ },
+ }
+}
+
+function stringValue(value: unknown): string {
+ return typeof value === 'string' ? value.trim() : ''
+}
+
+function filterBySearch(resources: CloudResource[], search?: string): CloudResource[] {
+ const normalized = search?.trim().toLowerCase()
+ if (!normalized) return resources
+ return resources.filter((resource) => resource.name.toLowerCase().includes(normalized))
+}
+
+function isNotFound(error: unknown): boolean {
+ if (typeof error !== 'object' || error === null) return false
+ const candidate = error as {name?: string; $metadata?: {httpStatusCode?: number}}
+ return candidate.name === 'NoSuchEntityException' || candidate.$metadata?.httpStatusCode === 404
+}
diff --git a/packages/api/src/aws.ts b/packages/api/src/aws.ts
index e9ae0c9..f14e47d 100644
--- a/packages/api/src/aws.ts
+++ b/packages/api/src/aws.ts
@@ -4,6 +4,7 @@ import { EKSClient } from "@aws-sdk/client-eks";
import { EC2Client } from "@aws-sdk/client-ec2";
import { RDSClient } from "@aws-sdk/client-rds";
import { SecretsManagerClient } from "@aws-sdk/client-secrets-manager";
+import { IAMClient } from "@aws-sdk/client-iam";
const endpoint = process.env.FLOCI_ENDPOINT;
const region = process.env.AWS_REGION || "us-east-1";
@@ -39,6 +40,7 @@ export type AwsClients = {
ec2: EC2Client;
rds: RDSClient;
secretsManager: SecretsManagerClient;
+ iam: IAMClient;
};
export type AwsClientName = keyof AwsClients;
@@ -58,6 +60,7 @@ function buildClients(accountId: string): AwsClients {
ec2: new EC2Client(base),
rds: new RDSClient(base),
secretsManager: new SecretsManagerClient(base),
+ iam: new IAMClient(base),
};
}
@@ -89,3 +92,4 @@ export const eks = awsClients.eks;
export const ec2 = awsClients.ec2;
export const rds = awsClients.rds;
export const secretsManager = awsClients.secretsManager;
+export const iam = awsClients.iam;
diff --git a/packages/api/src/cloud-spi/iamSchema.ts b/packages/api/src/cloud-spi/iamSchema.ts
new file mode 100644
index 0000000..db7e816
--- /dev/null
+++ b/packages/api/src/cloud-spi/iamSchema.ts
@@ -0,0 +1,55 @@
+import type {CapabilitySchema, CloudProvider, ResourceActionName, ServiceSchema} from './types'
+
+const resourceActions: CapabilitySchema[] = [
+ {name: 'list', label: 'List users', enabled: true, status: 'available', runtimeRequired: true},
+ {name: 'create', label: 'Create user', enabled: true, status: 'available', runtimeRequired: true},
+ {name: 'delete', label: 'Delete user', enabled: true, status: 'available', runtimeRequired: true},
+ {name: 'inspect', label: 'Inspect user', enabled: true, status: 'available', runtimeRequired: false},
+]
+
+export function awsIamSchema(): ServiceSchema {
+ return {
+ cloud: 'aws',
+ service: 'identity',
+ displayName: 'AWS IAM users',
+ fields: [
+ {
+ name: 'userName',
+ label: 'User Name',
+ type: 'text',
+ required: true,
+ description: '1-64 letters, numbers, and +=,.@_- characters.',
+ validation: {
+ pattern: '^[A-Za-z0-9_+=,.@-]{1,64}$',
+ minLength: 1,
+ maxLength: 64,
+ message: 'Use a valid IAM user name: 1-64 letters, numbers, and +=,.@_- characters.',
+ },
+ },
+ {
+ name: 'path',
+ label: 'Path',
+ type: 'text',
+ required: false,
+ description: 'Optional IAM path, beginning and ending with /. Defaults to /.',
+ validation: {
+ pattern: '^/(?:[!-~]+/)?$',
+ maxLength: 512,
+ message: 'Use a valid IAM path: begin and end with / and use only printable ASCII characters.',
+ },
+ },
+ ],
+ actions: ['list', 'create', 'delete', 'inspect'],
+ capabilities: {resourceActions},
+ filters: [{name: 'search', label: 'Search', type: 'text', required: false}],
+ columns: [
+ {name: 'name', label: 'User Name'},
+ {name: 'type', label: 'Type'},
+ {name: 'createdAt', label: 'Created At'},
+ ],
+ }
+}
+
+export function iamSchemaFor(cloud: CloudProvider): ServiceSchema | null {
+ return cloud === 'aws' ? awsIamSchema() : null
+}
diff --git a/packages/api/src/cloud-spi/types.ts b/packages/api/src/cloud-spi/types.ts
index bf9da0d..998ac63 100644
--- a/packages/api/src/cloud-spi/types.ts
+++ b/packages/api/src/cloud-spi/types.ts
@@ -1,6 +1,6 @@
export type CloudProvider = 'aws' | 'azure' | 'gcp'
-export type CloudServiceType = 'storage' | 'k8s' | 'database' | 'serverless' | 'compute' | 'networking'
+export type CloudServiceType = 'storage' | 'k8s' | 'database' | 'serverless' | 'compute' | 'networking' | 'identity'
export type CloudAvailability = 'available' | 'coming_soon'
@@ -83,7 +83,7 @@ export interface CloudResource {
name: string
cloud: CloudProvider
service: CloudServiceType
- type: 'bucket' | 'container' | 'cluster' | 'db-instance' | 'cosmos-database' | 'instance' | 'image' | 'vpc' | 'lambda' | 'azure-function' | 'gcp-function'
+ type: 'bucket' | 'container' | 'cluster' | 'db-instance' | 'cosmos-database' | 'instance' | 'image' | 'vpc' | 'lambda' | 'azure-function' | 'gcp-function' | 'iam-user'
region: string | null
createdAt: string | null
status?: string | null
diff --git a/packages/api/src/cloudProxy.ts b/packages/api/src/cloudProxy.ts
index b9740c0..8417a58 100644
--- a/packages/api/src/cloudProxy.ts
+++ b/packages/api/src/cloudProxy.ts
@@ -11,6 +11,7 @@ import {GcpCloudFunctionsAdapter} from './adapter-gcp/GcpCloudFunctionsAdapter'
import {CloudProxyService} from './service/CloudProxyService'
import {AzureServerlessAdapter} from './adapter-azure/AzureServerlessAdapter'
import {AwsServerlessAdapter} from './adapter-aws/AwsServerlessAdapter'
+import {AwsIamAdapter} from './adapter-aws/AwsIamAdapter'
import {awsClientsForAccount, resolveAccountId} from './aws'
import {createEc2Service} from './services/ec2'
import {createEksService} from './services/eks'
@@ -33,6 +34,7 @@ export function createCloudProxyService(accountId?: string | null): CloudProxySe
new AwsComputeAdapter(ec2Service),
new AwsNetworkingAdapter(ec2Service),
new AwsServerlessAdapter(clients.lambda),
+ new AwsIamAdapter(clients.iam),
new AzureStorageAdapter(),
new AzureDatabaseAdapter(),
new GcpStorageAdapter(),
diff --git a/packages/api/src/routes/clouds.test.ts b/packages/api/src/routes/clouds.test.ts
index 49296be..b107224 100644
--- a/packages/api/src/routes/clouds.test.ts
+++ b/packages/api/src/routes/clouds.test.ts
@@ -2,6 +2,7 @@ import {describe, expect, test} from 'bun:test'
import {Hono} from 'hono'
import {azureDatabaseSchema} from '../cloud-spi/databaseSchema'
import {awsStorageSchema, azureStorageSchema} from '../cloud-spi/storageSchema'
+import {awsIamSchema} from '../cloud-spi/iamSchema'
import type {CloudResource, CloudServiceAdapter, CosmosContainer, CosmosItem, CosmosQueryResult, CreateResourceInput} from '../cloud-spi/types'
import {CloudAdapterRegistry} from '../registry/CloudAdapterRegistry'
import {CloudProxyService} from '../service/CloudProxyService'
@@ -68,6 +69,20 @@ describe('cloud schema routes', () => {
expect(body.fields[0].name).toBe('containerName')
})
+ test('returns AWS IAM schema through the identity service route', async () => {
+ const app = appWithRoutes([mockAdapter('aws', {
+ service: 'identity',
+ schema: awsIamSchema,
+ })])
+ const res = await app.request('/api/clouds/aws/services/identity/schema')
+ const body = await res.json()
+
+ expect(res.status).toBe(200)
+ expect(body.cloud).toBe('aws')
+ expect(body.service).toBe('identity')
+ expect(body.displayName).toBe('AWS IAM users')
+ })
+
test('returns Azure database schema when the adapter is registered', async () => {
const app = appWithRoutes([mockAdapter('azure', {
service: 'database',
@@ -312,4 +327,42 @@ describe('cloud schema routes', () => {
expect(body.code).toBe('operation_not_implemented')
expect(body.message).toBe('Operation is not implemented by the selected runtime')
})
+
+ test('normalizes adapter validation errors', async () => {
+ const app = appWithRoutes([mockAdapter('aws', {
+ service: 'identity',
+ schema: awsIamSchema,
+ create: async () => {
+ throw new Error('Use a valid IAM path: begin and end with /.')
+ },
+ })])
+
+ const res = await app.request('/api/clouds/aws/services/identity/resources', {
+ method: 'POST',
+ body: JSON.stringify({userName: 'alice', path: 'team'}),
+ })
+ const body = await res.json()
+
+ expect(res.status).toBe(400)
+ expect(body.code).toBe('invalid_request')
+ expect(body.message).toContain('Use a valid IAM path')
+ })
+
+ test('normalizes IAM delete conflicts', async () => {
+ const app = appWithRoutes([mockAdapter('aws', {
+ service: 'identity',
+ schema: awsIamSchema,
+ delete: async () => {
+ throw Object.assign(new Error('Cannot delete entity'), {name: 'DeleteConflictException'})
+ },
+ })])
+
+ const res = await app.request('/api/clouds/aws/services/identity/resources/alice', {method: 'DELETE'})
+ const body = await res.json()
+
+ expect(res.status).toBe(409)
+ expect(body.code).toBe('resource_conflict')
+ expect(body.message).toBe('IAM user has attached resources')
+ expect(body.detail).toContain('access keys')
+ })
})
diff --git a/packages/api/src/routes/clouds.ts b/packages/api/src/routes/clouds.ts
index 93cb831..63c0cc9 100644
--- a/packages/api/src/routes/clouds.ts
+++ b/packages/api/src/routes/clouds.ts
@@ -261,7 +261,7 @@ function isCloudProvider(value: string): value is CloudProvider {
}
function isServiceType(value: string): value is CloudServiceType {
- return value === 'storage' || value === 'k8s' || value === 'database' || value === 'serverless' || value === 'compute' || value === 'networking'
+ return value === 'storage' || value === 'k8s' || value === 'database' || value === 'serverless' || value === 'compute' || value === 'networking' || value === 'identity'
}
async function withRuntime(c: Context, handler: () => Promise): Promise {
@@ -274,10 +274,11 @@ async function withRuntime(c: Context, handler: () => Promise): Promis
}
function normalizeRuntimeError(err: unknown): {
- status: 400 | 404 | 501 | 502 | 503
+ status: 400 | 404 | 409 | 501 | 502 | 503
body: {error: string; code: string; message: string; detail?: string}
} {
const message = err instanceof Error ? err.message : 'Runtime request failed'
+ const errorName = err instanceof Error ? err.name : ''
if (message.includes('Cannot reach')) {
return errorResponse(503, 'runtime_unavailable', 'Runtime unavailable', message)
@@ -304,6 +305,15 @@ function normalizeRuntimeError(err: unknown): {
return errorResponse(501, 'operation_not_supported', 'Operation is not supported by this adapter', message)
}
+ if (errorName === 'DeleteConflictException') {
+ return errorResponse(
+ 409,
+ 'resource_conflict',
+ 'IAM user has attached resources',
+ 'Remove group memberships, access keys, certificates, MFA devices, and policies before deleting the user.',
+ )
+ }
+
if (message.includes('is required') || message.includes('Use a valid')) {
return errorResponse(400, 'invalid_request', message)
}
@@ -312,12 +322,12 @@ function normalizeRuntimeError(err: unknown): {
}
function errorResponse(
- status: 400 | 404 | 501 | 502 | 503,
+ status: 400 | 404 | 409 | 501 | 502 | 503,
code: string,
message: string,
detail?: string,
): {
- status: 400 | 404 | 501 | 502 | 503
+ status: 400 | 404 | 409 | 501 | 502 | 503
body: {error: string; code: string; message: string; detail?: string}
} {
return {
diff --git a/packages/api/src/service/CloudProxyService.ts b/packages/api/src/service/CloudProxyService.ts
index 826bf12..ebe9e3c 100644
--- a/packages/api/src/service/CloudProxyService.ts
+++ b/packages/api/src/service/CloudProxyService.ts
@@ -20,6 +20,7 @@ import {CloudAdapterRegistry} from '../registry/CloudAdapterRegistry'
import {serverlessSchemaFor} from '../cloud-spi/serverlessSchema'
import {k8sSchemaFor} from '../cloud-spi/eksSchema'
import {databaseSchemaFor} from '../cloud-spi/databaseSchema'
+import {iamSchemaFor} from '../cloud-spi/iamSchema'
import {azureEndpoint} from '../azure'
import {checkGcpRuntime, gcpEndpoint} from '../gcp'
@@ -73,6 +74,12 @@ export class CloudProxyService {
displayName: 'Networking',
availability: this.registry.get(cloud, 'networking') ? 'available' : 'coming_soon',
})
+ services.push({
+ cloud,
+ service: 'identity',
+ displayName: 'Identity',
+ availability: this.registry.get(cloud, 'identity') ? 'available' : 'coming_soon',
+ })
return services
}
@@ -83,6 +90,7 @@ export class CloudProxyService {
if (service === 'k8s') return k8sSchemaFor(cloud)
if (service === 'database') return databaseSchemaFor(cloud)
if (service === 'serverless') return serverlessSchemaFor(cloud)
+ if (service === 'identity') return iamSchemaFor(cloud)
return null
}
diff --git a/packages/frontend/src/components/DynamicResourceView.tsx b/packages/frontend/src/components/DynamicResourceView.tsx
index 706b77b..2410b41 100644
--- a/packages/frontend/src/components/DynamicResourceView.tsx
+++ b/packages/frontend/src/components/DynamicResourceView.tsx
@@ -355,6 +355,8 @@ function resourceCreateLabel(schema: ServiceSchema): string {
return "Create container";
if (schema.cloud === "azure" && schema.service === "database")
return "Create database";
+ if (schema.cloud === "aws" && schema.service === "identity")
+ return "Create user";
return "Create resource";
}
diff --git a/packages/frontend/src/components/Layout.tsx b/packages/frontend/src/components/Layout.tsx
index 31a1891..5624618 100644
--- a/packages/frontend/src/components/Layout.tsx
+++ b/packages/frontend/src/components/Layout.tsx
@@ -9,6 +9,7 @@ import {
Network,
Search,
Server,
+ ShieldCheck,
Sun,
Table2,
Zap,
@@ -40,6 +41,7 @@ const CLOUD_SERVICE_ICONS = {
compute: Server,
networking: Network,
serverless: Zap,
+ identity: ShieldCheck,
} satisfies Record
type CloudSidebarService = keyof typeof CLOUD_SERVICE_ICONS
@@ -50,6 +52,7 @@ const CLOUD_SERVICE_ITEMS: Array<{name: CloudSidebarService; label: string; rout
{name: 'database', label: 'Database', route: 'database'},
{name: 'compute', label: 'Compute', route: 'compute'},
{name: 'networking', label: 'Networking', route: 'networking'},
+ {name: 'identity', label: 'Identity', route: 'identity'},
{name: 'secretsmanager', label: 'Secrets Manager', route: '/secretsmanager'},
{name: 'serverless', label: 'Serverless', route: 'serverless'},
{name: 'queue', label: 'Queue'},
@@ -70,6 +73,7 @@ function CloudServiceNav() {
|| (service.name === 'secretsmanager' && cloud === 'aws')
|| (service.name === 'database' && (cloud === 'aws' || cloud === 'azure'))
|| ((service.name === 'k8s' || service.name === 'compute' || service.name === 'networking') && cloud === 'aws')
+ || (service.name === 'identity' && cloud === 'aws')
|| (service.name === 'serverless' && (cloud === 'aws' || cloud === 'azure'))
if (service.route && available) {
const target = service.route.startsWith('/') ? service.route : `/cloud-explorer/${cloud}/${service.route}`
diff --git a/packages/frontend/src/pages/CloudExplorerPage.tsx b/packages/frontend/src/pages/CloudExplorerPage.tsx
index 37efef0..a7a5583 100644
--- a/packages/frontend/src/pages/CloudExplorerPage.tsx
+++ b/packages/frontend/src/pages/CloudExplorerPage.tsx
@@ -102,7 +102,7 @@ function normalizeCloud(value?: string): CloudProvider | null {
}
function normalizeService(value?: string): CloudServiceType | null {
- return value === 'storage' || value === 'k8s' || value === 'database' || value === 'compute' || value === 'networking' || value === 'serverless' ? value : null
+ return value === 'storage' || value === 'k8s' || value === 'database' || value === 'compute' || value === 'networking' || value === 'serverless' || value === 'identity' ? value : null
}
function ServiceInfoDialog({
@@ -256,6 +256,7 @@ function connectionValue(status?: CloudStatus, loading?: boolean): string {
function serviceLabel(service: CloudServiceType): string {
if (service === 'k8s') return 'k8s Engine'
if (service === 'serverless') return 'Serverless'
+ if (service === 'identity') return 'Identity'
return service.charAt(0).toUpperCase() + service.slice(1)
}
@@ -265,5 +266,6 @@ function limitationCopy(cloud: CloudProvider, service: CloudServiceType): string
if (cloud === 'azure' && service === 'database') return 'Cosmos DB uses a richer panel below for databases, containers, items, and SQL queries; a fully normalized database model is still evolving.'
if (cloud === 'aws' && service === 'compute') return 'Compute workflows still rely on AWS-specific forms for dependent infrastructure choices such as VPC, subnet, and security group.'
if (cloud === 'aws' && service === 'networking') return 'Networking uses AWS-specific operational panels because many actions require nested workflows instead of a flat generic form.'
+ if (cloud === 'aws' && service === 'identity') return 'IAM user management is available through the normalized contract; roles, groups, policies, and credentials are not exposed yet.'
return 'This service is available through the current adapter, but the normalized contract is still expanding.'
}
diff --git a/packages/frontend/src/types/cloud.ts b/packages/frontend/src/types/cloud.ts
index 668a147..496a3e1 100644
--- a/packages/frontend/src/types/cloud.ts
+++ b/packages/frontend/src/types/cloud.ts
@@ -1,6 +1,6 @@
export type CloudProvider = 'aws' | 'azure' | 'gcp'
export type CloudAvailability = 'available' | 'coming_soon'
-export type CloudServiceType = 'storage' | 'k8s' | 'database' | 'compute' | 'networking' | 'serverless'
+export type CloudServiceType = 'storage' | 'k8s' | 'database' | 'compute' | 'networking' | 'serverless' | 'identity'
export interface CloudDescriptor {
id: CloudProvider
diff --git a/packages/frontend/src/types/resource.ts b/packages/frontend/src/types/resource.ts
index 71f464f..de878ec 100644
--- a/packages/frontend/src/types/resource.ts
+++ b/packages/frontend/src/types/resource.ts
@@ -5,7 +5,7 @@ export interface CloudResource {
name: string
cloud: CloudProvider
service: CloudServiceType
- type: 'bucket' | 'container' | 'cluster' | 'db-instance' | 'cosmos-database' | 'instance' | 'image' | 'vpc' | 'lambda' | "azure-function" | 'gcp-function';
+ type: 'bucket' | 'container' | 'cluster' | 'db-instance' | 'cosmos-database' | 'instance' | 'image' | 'vpc' | 'lambda' | "azure-function" | 'gcp-function' | 'iam-user';
region: string | null
createdAt: string | null
status?: string | null
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f04df5d..1673560 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -16,6 +16,9 @@ importers:
'@aws-sdk/client-eks':
specifier: ^3.1076.0
version: 3.1076.0
+ '@aws-sdk/client-iam':
+ specifier: ^3.1076.0
+ version: 3.1076.0
'@aws-sdk/client-lambda':
specifier: ^3.1076.0
version: 3.1076.0
@@ -157,6 +160,10 @@ packages:
resolution: {integrity: sha512-Hqr3hdwUgqWAQbdx6UDy81UBF+jdu3ws6RrykQ4YmBhe/XOKv57lt2lhkjgBuF9fiSw4UUl2XQteks9oxgm4XA==}
engines: {node: '>=20.0.0'}
+ '@aws-sdk/client-iam@3.1076.0':
+ resolution: {integrity: sha512-9joUyOTN8KZUTi2UIuvD+kyiYImM9E40cXwNYUvc2plV18OQ0GoXcBE3Xey6czc+rtsgvq6Grp3UHFGuxfZmoQ==}
+ engines: {node: '>=20.0.0'}
+
'@aws-sdk/client-lambda@3.1076.0':
resolution: {integrity: sha512-uueu0Do1dy3cm42AdjL3zivHoc5b55vOL3g6BjxUv2xMifSLKZi5Ilh5FhXVDdS/Uwxak/JH+vVQDDnEuQ52gA==}
engines: {node: '>=20.0.0'}
@@ -177,38 +184,70 @@ packages:
resolution: {integrity: sha512-4/1DtLwgLqzIg2uFzkFaFjMQHhhhwHIZN4PfziIVqXYX7koO78omuchQlLHyzQBw80l255dtmTA/J4W5yhb7zw==}
engines: {node: '>=20.0.0'}
+ '@aws-sdk/core@3.975.3':
+ resolution: {integrity: sha512-7ur3kCKuvPLqlsZ2XlvnNBVQ7KkpSu6Y6dOTwSPHLrFpTEfZM8isLBJc4cgv96WB7GifeVM436mpycwxBd2vEA==}
+ engines: {node: '>=20.0.0'}
+
'@aws-sdk/credential-provider-env@3.972.54':
resolution: {integrity: sha512-F4WQCG8GULIt+XrMHsqUM9dZc0eTwZM3HUWByOjIKOwBqJSzUxX8CFAtbgMvWfCua52FS1oi5FXarH3+khFq8A==}
engines: {node: '>=20.0.0'}
+ '@aws-sdk/credential-provider-env@3.972.59':
+ resolution: {integrity: sha512-Ny5e4Mfh3QPmiAc0AiUe+cbTXDlxkU3Rc+EpWOfyWeWEy6yp7Fa1KmfNeCc+1a8by9zQ9gtohmiQUkMPScF3ng==}
+ engines: {node: '>=20.0.0'}
+
'@aws-sdk/credential-provider-http@3.972.56':
resolution: {integrity: sha512-PiwHgEK2srdfi/lFveyQ+3w/qikyh6MKvuZAdlMC+dwQi4K5iVBr32oh7cugnYw+jA8iGtnQMhCGvLm/mqplmw==}
engines: {node: '>=20.0.0'}
+ '@aws-sdk/credential-provider-http@3.972.61':
+ resolution: {integrity: sha512-8jAjgStl5Ytq4+HF3X/9f+EmRinaRbGRRtQGktlPfBRVx73H+R1y48vIeXerQtYGFaUqkEp3fT6jP854rVO2yQ==}
+ engines: {node: '>=20.0.0'}
+
'@aws-sdk/credential-provider-ini@3.972.61':
resolution: {integrity: sha512-9CZWMjhBlgfKlqJk40R7kvMOLEIoOr3HJ1J/ELjAH9H5LzR4bCxLujPVM/1PKAEjzSZKZCxWOalm7JUGZbhQqw==}
engines: {node: '>=20.0.0'}
- '@aws-sdk/credential-provider-login@3.972.60':
- resolution: {integrity: sha512-Ak6OOrCbXvACyxLFIP1mcS+JTLS9ZpW1ZqyBtqu6axvdpsbG1gVNhUlAfQq8TK/gar/h2w35LrzlQU0PcUzJpw==}
+ '@aws-sdk/credential-provider-ini@3.973.4':
+ resolution: {integrity: sha512-e6ZvVsj90aRALf1kHP+J4iqC1496ZpVgqI/+u0LJ5HL7q7ATauGy4gdDvRCP13L1pN/fMiZLah162PGIYkbUVQ==}
+ engines: {node: '>=20.0.0'}
+
+ '@aws-sdk/credential-provider-login@3.972.66':
+ resolution: {integrity: sha512-g2fsqm87r/nKthLZ0VkkDBElkGg0PvSa8d97HQ6EilMbJTZ6hxa8FxkSZyJfgPfFdZn0TTmkOffQmTSUcAHIng==}
engines: {node: '>=20.0.0'}
'@aws-sdk/credential-provider-node@3.972.63':
resolution: {integrity: sha512-YmgWtTPZDStyT74ApSHpApD3r7W9znsc+WEZjW0vceom+NAxRx9/F3TyukOKix8kJkPaa49aIREQJdpGeLDiEw==}
engines: {node: '>=20.0.0'}
+ '@aws-sdk/credential-provider-node@3.972.70':
+ resolution: {integrity: sha512-3xzvkGdykBunxqh8WudmUpSyLWvIhfI6aBQo1b5rb3mDO5mNLadK+0hiI0qBQBMVynJbfLO+Ajy9dztMwy9O8w==}
+ engines: {node: '>=20.0.0'}
+
'@aws-sdk/credential-provider-process@3.972.54':
resolution: {integrity: sha512-UNmUjtTnp3wH3YTZcctN7yK17S2AdaJpiNIdIf0l70hHOdGuDfdMxk62P5rt64GwGm6qkFOYG0js/cU6cdZDdg==}
engines: {node: '>=20.0.0'}
+ '@aws-sdk/credential-provider-process@3.972.59':
+ resolution: {integrity: sha512-DlZF2/MhLlatDdlrIy3CUCpfdbLrKx+3SMjVo+WyHnPpwzkc/M3vwAHw4OVJf7DMvO+4vfRqSCMc/E9I1auN0g==}
+ engines: {node: '>=20.0.0'}
+
'@aws-sdk/credential-provider-sso@3.972.60':
resolution: {integrity: sha512-zH8SvJkTRw1Kb7GARjGPjzkQPfL3jDi/OxK32I5SQq7bgwgFHJZaoDEwkEvFlfNfpByM6n4Rs20Y1mLyOdb6ag==}
engines: {node: '>=20.0.0'}
+ '@aws-sdk/credential-provider-sso@3.973.3':
+ resolution: {integrity: sha512-hmdDHoy2G5Es2e8IgelNMYUuSQI6uCIAKZMJ2u2PdKDhxvbk1uWD/g4+R7R5c/tJfKEB1+KjjWiaoCr/S+ZTiQ==}
+ engines: {node: '>=20.0.0'}
+
'@aws-sdk/credential-provider-web-identity@3.972.60':
resolution: {integrity: sha512-q2rJSQ/AMjemUS18OtQqczqSo2R6VjOCLmLVmLVcdrP5/AKoj632lGCrMUWQ6EgnaLMEHbq0UO4mWh/u6gjPhA==}
engines: {node: '>=20.0.0'}
+ '@aws-sdk/credential-provider-web-identity@3.972.65':
+ resolution: {integrity: sha512-gHQb/Kt0chjk/JQDa/GJDqmAvEuVn8n7z10wK2h0LFM9TUDRkohgOO4aEF+s2sBLM0br7Cl5W6P7phgjrrJvLQ==}
+ engines: {node: '>=20.0.0'}
+
'@aws-sdk/middleware-flexible-checksums@3.974.38':
resolution: {integrity: sha512-grguw9ssx2zVIn8/KpgQ7AQLjBE/YPwOns94n0seWkNWS3dJ7k7VFD2voAkh9Xvf8Azxw8Ajc5+KicdClucMqQ==}
engines: {node: '>=20.0.0'}
@@ -225,22 +264,34 @@ packages:
resolution: {integrity: sha512-KCf/UjbnzV3QIXR1C2MeE9eRUG8EUXUf0V4y65hf2bKWQXzol5f3I8wOhE6OBdEYohn8zHfnC/cyy5+s7/HwLw==}
engines: {node: '>=20.0.0'}
- '@aws-sdk/nested-clients@3.997.28':
- resolution: {integrity: sha512-1oG/mM3jmE2M3kad2zWJS6IKIY8hjRV4l5kAgg+xTQdLMTtehhcSucL/y4WqQpcHmQwi6+gRK8E46GYl1NBW9Q==}
+ '@aws-sdk/nested-clients@3.997.33':
+ resolution: {integrity: sha512-dVZOroI/r3/ENvqNGgjMPul+jjlz9GddfVusgTXlVjfZj5isibOxecLkGQbRPp8XOuX+RAfjXLFgPkD1JS5xrw==}
engines: {node: '>=20.0.0'}
'@aws-sdk/signature-v4-multi-region@3.996.38':
resolution: {integrity: sha512-C379Sk+MiFZCfWZphKlMyLHKxV22OjoGM5KJjj5IJNJcOCWL4IGIpnEGzv1FQiRwhYXfq55SJMfxlqPE08JJ9g==}
engines: {node: '>=20.0.0'}
+ '@aws-sdk/signature-v4-multi-region@3.996.41':
+ resolution: {integrity: sha512-QMUytg+FQMGouc8gHS00KoYih3+N6cqmVI/pQGOIo7Nr7OpQaiXjSYOuL+vsPZ1tymY4LAQ8MYcHJmws5LRxng==}
+ engines: {node: '>=20.0.0'}
+
'@aws-sdk/token-providers@3.1080.0':
resolution: {integrity: sha512-8PufAQvncWXvdZUvODbuyXa8l3aszefEzwSMBUcgheNbZOmJMcNn388Ebt/piVrUHyxKN1MlxnR2OonzTyZaGw==}
engines: {node: '>=20.0.0'}
+ '@aws-sdk/token-providers@3.1088.0':
+ resolution: {integrity: sha512-4ObatWt2qpJg5FBk4LOOKrTQYzaqeewAtdO3r9ZO8lH9YqLtpTzLyIdy0mJ+nVdfYOnqISkKNfmzP22bNDhwyw==}
+ engines: {node: '>=20.0.0'}
+
'@aws-sdk/types@3.973.15':
resolution: {integrity: sha512-IULn8uBV/SMtmOIANsm4WHXIOtVPBWfOWs3WGL0j/sI+KhaYehvOw0ET+9urnn8MBpiijuU/0JOpuwKOE451PQ==}
engines: {node: '>=20.0.0'}
+ '@aws-sdk/types@3.974.2':
+ resolution: {integrity: sha512-3W6IUtSxFbH6X7Wb7DzGCV5QiFQsd0g8bOfntpmDxQlzBoKWUMBu/JPQR0DwkE+Hpnxd6db1tXbOwdeHddG6cA==}
+ engines: {node: '>=20.0.0'}
+
'@aws-sdk/util-locate-window@3.965.8':
resolution: {integrity: sha512-uUbMs1cBZPafD0ohUj6EwNf0fPZ534NvBxHox4hjX+0Rxq5paSYUem7+hi833pYrzrcnBATKIYpR02MDXT5M9g==}
engines: {node: '>=20.0.0'}
@@ -249,6 +300,10 @@ packages:
resolution: {integrity: sha512-ezbwz9WpuLctm6o7P2t2naDhVVPI5jFGrVefVybhcKGjU57VIyT46pQVO0RI2RYkUdhdj2Z9uSIlAzGZE9NW9A==}
engines: {node: '>=20.0.0'}
+ '@aws-sdk/xml-builder@3.972.36':
+ resolution: {integrity: sha512-RdGmS1GLrtaTOLE1ElSluMldNrpk9Emq6uYs8SS8iHlu5xTAmM9rRkM91o48+rIRryBtyO9t+uLYCoMG6jVMVA==}
+ engines: {node: '>=20.0.0'}
+
'@aws/lambda-invoke-store@0.3.0':
resolution: {integrity: sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==}
engines: {node: '>=18.0.0'}
@@ -509,6 +564,14 @@ packages:
resolution: {integrity: sha512-qoiY4nrk5OCu1+eIR1VB8l5DmON/oKiqrd5zZFAhXJXjJlLWQusKEW/SkBDAtGDcPaz86m9kfcE1lngU0GlM6A==}
engines: {node: '>=18.0.0'}
+ '@smithy/core@3.29.5':
+ resolution: {integrity: sha512-i0dk2t5B+CwV/dcJdUHILYkOQF5lof8f44dFCfDWToGCxjT9YQ+CgHqTAvJxzc3+zqQwm2QtVoJ5IqiNar/CnQ==}
+ engines: {node: '>=18.0.0'}
+
+ '@smithy/credential-provider-imds@4.4.10':
+ resolution: {integrity: sha512-MJenAe4OKRZUo1LdYYFDCsSHxaHvInIU/z52GsheO9vl1/VSySVCr0zkyKD6TFiGkSUaWGxvKZ/70OvgUZR5HQ==}
+ engines: {node: '>=18.0.0'}
+
'@smithy/credential-provider-imds@4.4.6':
resolution: {integrity: sha512-B2WQ/PV/H6Jeg3lrIq6bKUfa6Hy01mtK7CGs6lhjzHA6k4aagldH6T6eEjnzKl4HI0cJnAsxfJ19pgb5PV+CVQ==}
engines: {node: '>=18.0.0'}
@@ -517,6 +580,10 @@ packages:
resolution: {integrity: sha512-CwCc/7SMTj45y97MUnDTbTaxvtAsiNNRm81z3abROIuMbMsC2Iy5EKfkkVdsKrz8WExQAAMx1EJapq+9j4fFTQ==}
engines: {node: '>=18.0.0'}
+ '@smithy/fetch-http-handler@5.6.7':
+ resolution: {integrity: sha512-3zpg8yqqyXzoK2TsRDdkqVOj2RDBFfLXwCczOZ5c7TWB4eiaebfSCsbMjDPYB3PJ9ihV62QaeadZ+wLadZtNGA==}
+ engines: {node: '>=18.0.0'}
+
'@smithy/is-array-buffer@2.2.0':
resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==}
engines: {node: '>=14.0.0'}
@@ -525,14 +592,26 @@ packages:
resolution: {integrity: sha512-qZTa4gQFUo8RM02rk6q5UVTDLNrQ1oS20LsepBzqq1QBVc/EHJ03OOUADcqMZiXHArW+Y7+OGY0BpdTwZRq/Yg==}
engines: {node: '>=18.0.0'}
+ '@smithy/node-http-handler@4.9.7':
+ resolution: {integrity: sha512-wCU8HCLjAtAVqxxe0j2xff9LcEPw3yjBbg5IdQDIYFnxnPxbxcSLc7rgex7kqm9L/WYOnJEgaWQlfDkZleozMA==}
+ engines: {node: '>=18.0.0'}
+
'@smithy/signature-v4@5.6.2':
resolution: {integrity: sha512-QgHflghMoPxCJ9axiCVh8KZfbC9fuP6vkXXyK//E3cq7nLaSSyyLj0GAoqVWezYeDQmXIZhmlRvLE16jsqDK6g==}
engines: {node: '>=18.0.0'}
+ '@smithy/signature-v4@5.6.6':
+ resolution: {integrity: sha512-efP6DN3UTFrzIsGO42/xcabv8jU7+9nwEdphFUH7yL0k010ERyAWaO41KFQIDLcFZLZ8xzIQr4wplFxNzslSGQ==}
+ engines: {node: '>=18.0.0'}
+
'@smithy/types@4.15.1':
resolution: {integrity: sha512-x3L0XSACF6UYzKpa9biqiRMgvH5+wnFFew9Tm/grFYqgaupPwx/+ojDPpPJM8dZON3S9tjz5U+PQYsCBd1Mw5Q==}
engines: {node: '>=18.0.0'}
+ '@smithy/types@4.16.1':
+ resolution: {integrity: sha512-0JFs3V2y2M9tKW5na/qxe69Zv+uxLMO7QBbhxF/FHu/Gp2NFZAAL9tWl9PU02xxo07pb3G9FTyjNc6D5uZrJIg==}
+ engines: {node: '>=18.0.0'}
+
'@smithy/util-buffer-from@2.2.0':
resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==}
engines: {node: '>=14.0.0'}
@@ -1302,16 +1381,16 @@ snapshots:
'@aws-crypto/util@5.2.0':
dependencies:
- '@aws-sdk/types': 3.973.15
+ '@aws-sdk/types': 3.974.2
'@smithy/util-utf8': 2.3.0
tslib: 2.8.1
'@aws-sdk/checksums@3.1000.13':
dependencies:
- '@aws-sdk/core': 3.974.28
- '@aws-sdk/types': 3.973.15
- '@smithy/core': 3.29.1
- '@smithy/types': 4.15.1
+ '@aws-sdk/core': 3.975.3
+ '@aws-sdk/types': 3.974.2
+ '@smithy/core': 3.29.5
+ '@smithy/types': 4.16.1
tslib: 2.8.1
'@aws-sdk/client-ec2@3.1076.0':
@@ -1341,6 +1420,19 @@ snapshots:
'@smithy/types': 4.15.1
tslib: 2.8.1
+ '@aws-sdk/client-iam@3.1076.0':
+ dependencies:
+ '@aws-crypto/sha256-browser': 5.2.0
+ '@aws-crypto/sha256-js': 5.2.0
+ '@aws-sdk/core': 3.975.3
+ '@aws-sdk/credential-provider-node': 3.972.70
+ '@aws-sdk/types': 3.974.2
+ '@smithy/core': 3.29.5
+ '@smithy/fetch-http-handler': 5.6.7
+ '@smithy/node-http-handler': 4.9.7
+ '@smithy/types': 4.16.1
+ tslib: 2.8.1
+
'@aws-sdk/client-lambda@3.1076.0':
dependencies:
'@aws-crypto/sha256-browser': 5.2.0
@@ -1409,47 +1501,92 @@ snapshots:
bowser: 2.14.1
tslib: 2.8.1
+ '@aws-sdk/core@3.975.3':
+ dependencies:
+ '@aws-sdk/types': 3.974.2
+ '@aws-sdk/xml-builder': 3.972.36
+ '@aws/lambda-invoke-store': 0.3.0
+ '@smithy/core': 3.29.5
+ '@smithy/signature-v4': 5.6.6
+ '@smithy/types': 4.16.1
+ bowser: 2.14.1
+ tslib: 2.8.1
+
'@aws-sdk/credential-provider-env@3.972.54':
dependencies:
- '@aws-sdk/core': 3.974.28
- '@aws-sdk/types': 3.973.15
- '@smithy/core': 3.29.1
- '@smithy/types': 4.15.1
+ '@aws-sdk/core': 3.975.3
+ '@aws-sdk/types': 3.974.2
+ '@smithy/core': 3.29.5
+ '@smithy/types': 4.16.1
+ tslib: 2.8.1
+
+ '@aws-sdk/credential-provider-env@3.972.59':
+ dependencies:
+ '@aws-sdk/core': 3.975.3
+ '@aws-sdk/types': 3.974.2
+ '@smithy/core': 3.29.5
+ '@smithy/types': 4.16.1
tslib: 2.8.1
'@aws-sdk/credential-provider-http@3.972.56':
dependencies:
- '@aws-sdk/core': 3.974.28
- '@aws-sdk/types': 3.973.15
- '@smithy/core': 3.29.1
- '@smithy/fetch-http-handler': 5.6.3
- '@smithy/node-http-handler': 4.9.3
- '@smithy/types': 4.15.1
+ '@aws-sdk/core': 3.975.3
+ '@aws-sdk/types': 3.974.2
+ '@smithy/core': 3.29.5
+ '@smithy/fetch-http-handler': 5.6.7
+ '@smithy/node-http-handler': 4.9.7
+ '@smithy/types': 4.16.1
+ tslib: 2.8.1
+
+ '@aws-sdk/credential-provider-http@3.972.61':
+ dependencies:
+ '@aws-sdk/core': 3.975.3
+ '@aws-sdk/types': 3.974.2
+ '@smithy/core': 3.29.5
+ '@smithy/fetch-http-handler': 5.6.7
+ '@smithy/node-http-handler': 4.9.7
+ '@smithy/types': 4.16.1
tslib: 2.8.1
'@aws-sdk/credential-provider-ini@3.972.61':
dependencies:
- '@aws-sdk/core': 3.974.28
- '@aws-sdk/credential-provider-env': 3.972.54
- '@aws-sdk/credential-provider-http': 3.972.56
- '@aws-sdk/credential-provider-login': 3.972.60
- '@aws-sdk/credential-provider-process': 3.972.54
- '@aws-sdk/credential-provider-sso': 3.972.60
- '@aws-sdk/credential-provider-web-identity': 3.972.60
- '@aws-sdk/nested-clients': 3.997.28
- '@aws-sdk/types': 3.973.15
- '@smithy/core': 3.29.1
- '@smithy/credential-provider-imds': 4.4.6
- '@smithy/types': 4.15.1
+ '@aws-sdk/core': 3.975.3
+ '@aws-sdk/credential-provider-env': 3.972.59
+ '@aws-sdk/credential-provider-http': 3.972.61
+ '@aws-sdk/credential-provider-login': 3.972.66
+ '@aws-sdk/credential-provider-process': 3.972.59
+ '@aws-sdk/credential-provider-sso': 3.973.3
+ '@aws-sdk/credential-provider-web-identity': 3.972.65
+ '@aws-sdk/nested-clients': 3.997.33
+ '@aws-sdk/types': 3.974.2
+ '@smithy/core': 3.29.5
+ '@smithy/credential-provider-imds': 4.4.10
+ '@smithy/types': 4.16.1
+ tslib: 2.8.1
+
+ '@aws-sdk/credential-provider-ini@3.973.4':
+ dependencies:
+ '@aws-sdk/core': 3.975.3
+ '@aws-sdk/credential-provider-env': 3.972.59
+ '@aws-sdk/credential-provider-http': 3.972.61
+ '@aws-sdk/credential-provider-login': 3.972.66
+ '@aws-sdk/credential-provider-process': 3.972.59
+ '@aws-sdk/credential-provider-sso': 3.973.3
+ '@aws-sdk/credential-provider-web-identity': 3.972.65
+ '@aws-sdk/nested-clients': 3.997.33
+ '@aws-sdk/types': 3.974.2
+ '@smithy/core': 3.29.5
+ '@smithy/credential-provider-imds': 4.4.10
+ '@smithy/types': 4.16.1
tslib: 2.8.1
- '@aws-sdk/credential-provider-login@3.972.60':
+ '@aws-sdk/credential-provider-login@3.972.66':
dependencies:
- '@aws-sdk/core': 3.974.28
- '@aws-sdk/nested-clients': 3.997.28
- '@aws-sdk/types': 3.973.15
- '@smithy/core': 3.29.1
- '@smithy/types': 4.15.1
+ '@aws-sdk/core': 3.975.3
+ '@aws-sdk/nested-clients': 3.997.33
+ '@aws-sdk/types': 3.974.2
+ '@smithy/core': 3.29.5
+ '@smithy/types': 4.16.1
tslib: 2.8.1
'@aws-sdk/credential-provider-node@3.972.63':
@@ -1466,31 +1603,72 @@ snapshots:
'@smithy/types': 4.15.1
tslib: 2.8.1
+ '@aws-sdk/credential-provider-node@3.972.70':
+ dependencies:
+ '@aws-sdk/credential-provider-env': 3.972.59
+ '@aws-sdk/credential-provider-http': 3.972.61
+ '@aws-sdk/credential-provider-ini': 3.973.4
+ '@aws-sdk/credential-provider-process': 3.972.59
+ '@aws-sdk/credential-provider-sso': 3.973.3
+ '@aws-sdk/credential-provider-web-identity': 3.972.65
+ '@aws-sdk/types': 3.974.2
+ '@smithy/core': 3.29.5
+ '@smithy/credential-provider-imds': 4.4.10
+ '@smithy/types': 4.16.1
+ tslib: 2.8.1
+
'@aws-sdk/credential-provider-process@3.972.54':
dependencies:
- '@aws-sdk/core': 3.974.28
- '@aws-sdk/types': 3.973.15
- '@smithy/core': 3.29.1
- '@smithy/types': 4.15.1
+ '@aws-sdk/core': 3.975.3
+ '@aws-sdk/types': 3.974.2
+ '@smithy/core': 3.29.5
+ '@smithy/types': 4.16.1
+ tslib: 2.8.1
+
+ '@aws-sdk/credential-provider-process@3.972.59':
+ dependencies:
+ '@aws-sdk/core': 3.975.3
+ '@aws-sdk/types': 3.974.2
+ '@smithy/core': 3.29.5
+ '@smithy/types': 4.16.1
tslib: 2.8.1
'@aws-sdk/credential-provider-sso@3.972.60':
dependencies:
- '@aws-sdk/core': 3.974.28
- '@aws-sdk/nested-clients': 3.997.28
+ '@aws-sdk/core': 3.975.3
+ '@aws-sdk/nested-clients': 3.997.33
'@aws-sdk/token-providers': 3.1080.0
- '@aws-sdk/types': 3.973.15
- '@smithy/core': 3.29.1
- '@smithy/types': 4.15.1
+ '@aws-sdk/types': 3.974.2
+ '@smithy/core': 3.29.5
+ '@smithy/types': 4.16.1
+ tslib: 2.8.1
+
+ '@aws-sdk/credential-provider-sso@3.973.3':
+ dependencies:
+ '@aws-sdk/core': 3.975.3
+ '@aws-sdk/nested-clients': 3.997.33
+ '@aws-sdk/token-providers': 3.1088.0
+ '@aws-sdk/types': 3.974.2
+ '@smithy/core': 3.29.5
+ '@smithy/types': 4.16.1
tslib: 2.8.1
'@aws-sdk/credential-provider-web-identity@3.972.60':
dependencies:
- '@aws-sdk/core': 3.974.28
- '@aws-sdk/nested-clients': 3.997.28
- '@aws-sdk/types': 3.973.15
- '@smithy/core': 3.29.1
- '@smithy/types': 4.15.1
+ '@aws-sdk/core': 3.975.3
+ '@aws-sdk/nested-clients': 3.997.33
+ '@aws-sdk/types': 3.974.2
+ '@smithy/core': 3.29.5
+ '@smithy/types': 4.16.1
+ tslib: 2.8.1
+
+ '@aws-sdk/credential-provider-web-identity@3.972.65':
+ dependencies:
+ '@aws-sdk/core': 3.975.3
+ '@aws-sdk/nested-clients': 3.997.33
+ '@aws-sdk/types': 3.974.2
+ '@smithy/core': 3.29.5
+ '@smithy/types': 4.16.1
tslib: 2.8.1
'@aws-sdk/middleware-flexible-checksums@3.974.38':
@@ -1525,15 +1703,15 @@ snapshots:
'@smithy/types': 4.15.1
tslib: 2.8.1
- '@aws-sdk/nested-clients@3.997.28':
+ '@aws-sdk/nested-clients@3.997.33':
dependencies:
- '@aws-sdk/core': 3.974.28
- '@aws-sdk/signature-v4-multi-region': 3.996.38
- '@aws-sdk/types': 3.973.15
- '@smithy/core': 3.29.1
- '@smithy/fetch-http-handler': 5.6.3
- '@smithy/node-http-handler': 4.9.3
- '@smithy/types': 4.15.1
+ '@aws-sdk/core': 3.975.3
+ '@aws-sdk/signature-v4-multi-region': 3.996.41
+ '@aws-sdk/types': 3.974.2
+ '@smithy/core': 3.29.5
+ '@smithy/fetch-http-handler': 5.6.7
+ '@smithy/node-http-handler': 4.9.7
+ '@smithy/types': 4.16.1
tslib: 2.8.1
'@aws-sdk/signature-v4-multi-region@3.996.38':
@@ -1543,13 +1721,29 @@ snapshots:
'@smithy/types': 4.15.1
tslib: 2.8.1
+ '@aws-sdk/signature-v4-multi-region@3.996.41':
+ dependencies:
+ '@aws-sdk/types': 3.974.2
+ '@smithy/signature-v4': 5.6.6
+ '@smithy/types': 4.16.1
+ tslib: 2.8.1
+
'@aws-sdk/token-providers@3.1080.0':
dependencies:
- '@aws-sdk/core': 3.974.28
- '@aws-sdk/nested-clients': 3.997.28
- '@aws-sdk/types': 3.973.15
- '@smithy/core': 3.29.1
- '@smithy/types': 4.15.1
+ '@aws-sdk/core': 3.975.3
+ '@aws-sdk/nested-clients': 3.997.33
+ '@aws-sdk/types': 3.974.2
+ '@smithy/core': 3.29.5
+ '@smithy/types': 4.16.1
+ tslib: 2.8.1
+
+ '@aws-sdk/token-providers@3.1088.0':
+ dependencies:
+ '@aws-sdk/core': 3.975.3
+ '@aws-sdk/nested-clients': 3.997.33
+ '@aws-sdk/types': 3.974.2
+ '@smithy/core': 3.29.5
+ '@smithy/types': 4.16.1
tslib: 2.8.1
'@aws-sdk/types@3.973.15':
@@ -1557,13 +1751,23 @@ snapshots:
'@smithy/types': 4.15.1
tslib: 2.8.1
+ '@aws-sdk/types@3.974.2':
+ dependencies:
+ '@smithy/types': 4.16.1
+ tslib: 2.8.1
+
'@aws-sdk/util-locate-window@3.965.8':
dependencies:
tslib: 2.8.1
'@aws-sdk/xml-builder@3.972.33':
dependencies:
- '@smithy/types': 4.15.1
+ '@smithy/types': 4.16.1
+ tslib: 2.8.1
+
+ '@aws-sdk/xml-builder@3.972.36':
+ dependencies:
+ '@smithy/types': 4.16.1
tslib: 2.8.1
'@aws/lambda-invoke-store@0.3.0': {}
@@ -1818,10 +2022,21 @@ snapshots:
'@smithy/types': 4.15.1
tslib: 2.8.1
+ '@smithy/core@3.29.5':
+ dependencies:
+ '@smithy/types': 4.16.1
+ tslib: 2.8.1
+
+ '@smithy/credential-provider-imds@4.4.10':
+ dependencies:
+ '@smithy/core': 3.29.5
+ '@smithy/types': 4.16.1
+ tslib: 2.8.1
+
'@smithy/credential-provider-imds@4.4.6':
dependencies:
- '@smithy/core': 3.29.1
- '@smithy/types': 4.15.1
+ '@smithy/core': 3.29.5
+ '@smithy/types': 4.16.1
tslib: 2.8.1
'@smithy/fetch-http-handler@5.6.3':
@@ -1830,6 +2045,12 @@ snapshots:
'@smithy/types': 4.15.1
tslib: 2.8.1
+ '@smithy/fetch-http-handler@5.6.7':
+ dependencies:
+ '@smithy/core': 3.29.5
+ '@smithy/types': 4.16.1
+ tslib: 2.8.1
+
'@smithy/is-array-buffer@2.2.0':
dependencies:
tslib: 2.8.1
@@ -1840,16 +2061,32 @@ snapshots:
'@smithy/types': 4.15.1
tslib: 2.8.1
+ '@smithy/node-http-handler@4.9.7':
+ dependencies:
+ '@smithy/core': 3.29.5
+ '@smithy/types': 4.16.1
+ tslib: 2.8.1
+
'@smithy/signature-v4@5.6.2':
dependencies:
- '@smithy/core': 3.29.1
- '@smithy/types': 4.15.1
+ '@smithy/core': 3.29.5
+ '@smithy/types': 4.16.1
+ tslib: 2.8.1
+
+ '@smithy/signature-v4@5.6.6':
+ dependencies:
+ '@smithy/core': 3.29.5
+ '@smithy/types': 4.16.1
tslib: 2.8.1
'@smithy/types@4.15.1':
dependencies:
tslib: 2.8.1
+ '@smithy/types@4.16.1':
+ dependencies:
+ tslib: 2.8.1
+
'@smithy/util-buffer-from@2.2.0':
dependencies:
'@smithy/is-array-buffer': 2.2.0