Skip to content

feat: Integrate Resource Browsers#183

Open
peter-smith-phd wants to merge 23 commits into
mainfrom
integrate-resource-browsers
Open

feat: Integrate Resource Browsers#183
peter-smith-phd wants to merge 23 commits into
mainfrom
integrate-resource-browsers

Conversation

@peter-smith-phd

@peter-smith-phd peter-smith-phd commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

This PR introduce "Resource Browsers" into the LocalStack Toolkit, similar to the feature we already have in the LocalStack Console, but more aligned with the IDE-centric way of viewing resources.

The LocalStack View Container now contains three views:

  1. The "Explore" view allows the user to explore their running LocalStack Instances, as well as explore their AWS Cloud Profiles. Clicking on an item in this view will update the content of the "Resources" view (see next).
  2. The "Resources" view allows the user to explore the resources (e.g. Lambda functions, S3 buckets, etc) in the selected region. Clicking on an item in this view will update the content of the "Resource Details" view (see next).
  3. The "Resource Details" view show the "describe" information from the selected resource.

Notes:

  • This PR is very long, because it's the result of combining the existing LocalStack Toolkit view, with an open source project I started last year. I combined the features, then added some usability improvements (e.g. customized views to restrict how much is shown, customizing which regions and profiles are shown, etc). I expect future PRs to be very small in comparison.
  • I also spent several days reviewing the code changes, making sure they followed a good architecture, used appropriate naming, and fit nicely into the existing LocalStack Toolkit. I wanted to ensure that future changes (by human or agent) will be easy to make, rather than adding to a hard-to-maintain codebase.
  • If you want to understand the code in more detail, I suggest reading openspec/changes/archive/2026-06-23-integrate-resource-browser/proposal.md and openspec/changes/archive/2026-06-23-integrate-resource-browser/design.md for an overview.
  • Right now, only 17 AWS services are supported, but this will be increased over time.
  • The "Workspace IaC" entry is non-functional right now, but will be extended over time. I left it in, largely for marketing purposes.
  • I want to add more testing, including Playwright testing, but that will come in a later PR.
image

@peter-smith-phd peter-smith-phd changed the title Integrate Resource Browsers feat: Integrate Resource Browsers Jun 23, 2026
@peter-smith-phd peter-smith-phd force-pushed the integrate-resource-browsers branch from 923378e to 3d44caa Compare June 24, 2026 17:40
peter-smith-phd and others added 22 commits June 25, 2026 07:42
Design-time artifacts for merging AWS Inspector's resource-browsing
views into the LocalStack Toolkit: proposal, design, per-capability
specs (focus-model, localstack-explorer-view, resource-browser,
localstack-metamodel), tasks, and a captured metamodel sample.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ing cleanup

Explore view UX refinements (rounds 3-5):
- Title Case command/menu labels; "All Resources" -> "View All Resources"
- Cloud Profiles / region / view row actions shown as inline icons
  (pencil/pencil/plus/trash, plus pencil+trash on view rows) and in the
  right-click context menu; "Add View..." moved off a tree row
- Adaptive settings persistence: write to Workspace when a folder is open,
  else Global, fixing "Unable to write to Workspace Settings" with no folder

ESLint typing cleanup:
- Remove the ported-AWS-code exception block in eslint.config.mjs and fix the
  ~62 no-unsafe-* errors at the source: typed `memoize` generic, typed SDK
  client wrappers (getQueueAttributes/getTopicAttributes/listStackResources),
  typed INI parsing in awsConfig, and the metamodel JSON-parse boundary

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Implements Section 18 of integrate-resource-browsers:
- blank icon by default on tree rows (section headers excepted)
- Edit View action uses the gear icon
- invert profile visibility to opt-in (cloudProfiles.shown), default
  to the `default` profile (or first when none named default), with a
  "No profiles selected" placeholder for an explicit empty set
- omit the account-alias separator when the alias is empty
- rename CloudFormation selectors "CFN:" -> "Stack:"
- modal confirmation before removing a region or a view
- manual refresh buttons on the Resources and Resource Details views

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Resources/Details refresh recomputes the focus (re-queries the
  LocalStack metamodel) instead of re-rendering a cached structure.
- Route all service clients through AWSConfig.getClientConfig so the
  localstack profile's endpoint is honored — fixes the spurious
  "security token included in the request is invalid" against real AWS.
- Merge the service and resource-type tree levels into one row
  (label = service, dimmed description = resource type), one row per
  service/resource-type pair; service icon on that row, none on leaves.
- Views are defined by selecting service/resource-type pairs;
  SavedFilter now stores a flat { service, resourceType } list.
- Rewrite Resource Details as a themed webview table with per-FieldType
  formatting (JSON/long-text blocks, monospace ARNs); interactions
  deferred.
- Drop the blanket default "blank" tree icon, keeping only the one that
  aligns the instance's "View: All Resources" with "App Inspector".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Failed, rolled-back, and deleting stacks passed the previous status
filter but have no live resources (or resources lacking a
PhysicalResourceId), so clicking them threw "Cannot read properties of
undefined (reading 'split')" and the Resources view stayed empty.

Narrow listStacks to the stable, resource-bearing terminal states so
such stacks never appear in the Explore view. As defense-in-depth,
convertToServicesList now skips (and warns about) any individual
resource it can't map instead of aborting the whole stack; CfnStackModel
takes an explicit LogOutputChannel for those warnings.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add ported model tests completing task 3.5:
- awsConfig.test.ts: profile listing, region resolution (file + AWS_REGION
  precedence), endpoint_url surfacing, missing-file fallback
- cfnStackModel.test.ts: stack-resource -> Focus conversion, same-type
  grouping, and the per-resource skip/warn behavior (covers the
  previously-fatal missing-ResourceType case)

Sync the change's delta specs into openspec/specs/ (focus-model,
localstack-explorer-view, localstack-metamodel, resource-browser) and
archive the completed integrate-resource-browsers change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The archive commit captured tasks.md before the working-tree checkbox
updates were staged. Bring the archived task list in sync with the
actual completion state (3.5 model tests + the manual-verification
tasks).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rative providers

Introduce a static, coverage-derived service manifest as the single source of
truth for supported AWS services, a declarative provider engine, a build-time
detail-field generator, manifest-backed provider registration with a
completeness tracker, and Batch 1 (10 curated services, 26 resource types).

- manifest: build/generate-service-manifest.mjs -> resources/service-manifest.json
  (116 services); memoized loader + shared label mapping (stepfunctions -> states)
- declarative engine: defineService/defineResourceType + DeclarativeServiceProvider
  (list, identifier mapping incl. region-synthesized ARNs, path-walked detail,
  matchArn, CFN mapping); imperative ServiceProvider kept as the escape hatch
- detail-field generator: build/generate-detail-fields.mjs (importance heuristic)
- ProviderFactory resolves by manifest id, no generic fallback; default icon path
- cfnStackModel + metamodelFocus use the shared label mapping; Resources leaf
  tolerates non-ARN identifiers
- Batch 1: S3, API Gateway, SSM, Secrets Manager, Kinesis, CloudWatch Logs,
  EventBridge, KMS, Cognito (cognito-idp; IdentityPool deferred), ECR

Deferred: migrating the 7 imperative providers to declarative; manual emulator/
cloud verification. 105 tests passing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The per-service icons in the Resources view were hand-edited derivatives of
AWS's Architecture Icons; bundling AWS iconography in the product risks
violating AWS's icon terms of use. Remove the 6 derivative SVGs and the
per-service icon API, and instead show an icon that reflects the profile's
target: the LocalStack mark for LocalStack-targeted profiles and the built-in
`cloud` codicon for real AWS. Both are themed ThemeIcons with no AWS IP.

- Delete resources/icons/services/*.svg and ServiceProvider.getIconPath
  (plus the now-dead `context` plumbing through the provider chain)
- Add AWSConfig.getEndpointForProfile; resolve isLocalStack per profile in
  viewProvider and thread it to the service-and-resource-type row
- Update the resource-browser spec; add tree-item icon and
  getEndpointForProfile tests
- Archive the remove-aws-service-icons OpenSpec change

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Remove unused StandardModel/loadStandardModel and resources/focuses/
- Rename src/views/localstack → src/views/explore to match the view name
- Migrate internal "filter" terminology to "view" (cloud-profile views):
  SavedView/ViewScope, getProfileViews, saveProfileView, removeProfileView,
  ProfileViewTreeItem, resolveRegionViewFocus, command IDs
  localstack.{add,edit,remove}ProfileView, contextValue localstackProfileView,
  and config key cloudProfiles.views

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…efinitions

Migrate the seven hand-written ServiceProvider subclasses (CloudFormation,
DynamoDB, IAM, Lambda, SNS, SQS, Step Functions) to declarative
ServiceDefinitions executed by the engine, and delete the now-orphaned client
wrappers. IMPERATIVE_PROVIDERS is now an empty (documented) escape hatch.

Add a `list` detail-spec to the declarative engine/types so variable-length
sections (DynamoDB attributes/key schema, CloudFormation parameters/outputs)
can be expressed declaratively — the one capability the FieldSpec model
previously lacked.

Behavior preserved incl. CFN mapping (SQS queue-URL split, Lambda type-prefixed
names, States still unmapped) and metamodel op maps. Two intentional
normalizations to match existing declarative providers: no redundant
"Resource Type" row, and missing values render as "" rather than "N/A".

Add engine list-spec tests and a batch2 provider test suite.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
getRegionLongName threw for any region missing from its hardcoded table, so a
single such region in the running emulator's metamodel (e.g. cn-north-1)
aborted the whole "All Resources" view. It now degrades to the region code for
unknown regions, and the China partition (cn-north-1, cn-northwest-1) is added
to the table.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…esources

Opening an API Gateway or Cognito resource from a Stack view threw
"Cannot resolve resource type for ARN". The live `list` path and the
CloudFormation path synthesize ARNs differently, and these multi-type
services rely on the engine's "self" describe path, which requires the
synthesized ARN to string-equal the live `id` — but the CFN template
always injects an account segment the live `id` omits.

Fix the 4 types CloudFormation can support by re-encoding the
discriminating token via cfnResourceName and adding a direct describe()
(also removes the per-click re-list for the API Gateway types):
  - cognito userpool, apigateway restapi/apikey/usageplan

Drop the cfn mapping for the 4 it cannot: their PhysicalResourceId is
the leaf id only, without the parent pool/api id every describe API
needs, so CFN-origin resolution is impossible. They are now skipped and
logged in stack views (as the states definition does) rather than
throwing on click; live resources still resolve.
  - cognito userpoolclient/userpoolgroup, apigateway stage/authorizer

Add cfnRoundTrip.test.ts covering synthesize -> resolve -> describe for
the 4 fixed types and asserting the 4 dropped types are unmapped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
memoize() cached only a promise's resolved value, set inside .then(), so
concurrent callers each fired their own request before the first resolved
— defeating the cache exactly during tree expansion, when many nodes
resolve at once. Cached data also lived for the whole session with no way
to invalidate, so the refresh button replayed stale account ids / region
lists and never picked up re-authenticated credentials.

Cache the promise itself (concurrent callers now share one in-flight
request) and evict it on rejection so failures aren't served forever.
Add clear() per memoized function plus a registry-backed
clearMemoizedCaches(), and wire it into the refreshResources command so a
manual refresh re-queries AWS and rebuilds clients (also picking up an
endpoint change for the profile-only-keyed clients).

Add memoize.test.ts covering dedup, rejection-eviction, and clear paths.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Relocate the Resource Details webview from the left activity-bar container
to its own bottom-panel container (localstackPanel), giving the key/value
table room to breathe. Explore and Resources stay in the sidebar, now
split 50/50 by default (Resources size 2 -> 1).

Selecting a resource reveals the panel via WebviewView.show(true), which
brings the tab forward without stealing keyboard focus, so arrow-key
browsing in the Resources tree keeps working and details follow live. The
first reveal (before the view has resolved) falls back to the auto-
generated <viewId>.focus command to open the panel.

Also cap the Resource Details field-label column at 20% (was 33%); the
fixed table layout makes that a hard cap so long labels wrap rather than
widen the column.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Unpin all @aws-sdk/client-* from exact 3.901.0 to ^3.901.0 so they track
updates rather than staying on an old pin; re-resolving lands on 3.1070.0
(the newest version past the 7-day cooldown).

Regenerate pnpm-lock.yaml so a clean `pnpm install` passes the repo's
supply-chain policies:
- minimumReleaseAge: the previously-fresh @smithy transitive deps now
  resolve to aged versions automatically on re-resolution.
- no-downgrade: add version-scoped trustPolicyExclude for chokidar@4.0.3,
  semver@5.7.2, semver@6.3.1, and undici-types@6.21.0 — all confirmed
  benign provenance gaps (maintainers dropped npm provenance on those
  specific releases and restored it later), not takeovers.

Add keepNames to the esbuild config so class names survive bundling. The
AWS SDK bump triggered an esbuild identifier collision (e.g.
DescribeKeyCommand -> DescribeKeyCommand2) that broke the provider tests'
constructor.name dispatch.

Fix `vsce package`: compile no longer runs through npm-run-all, which
spawned the corepack-managed pnpm shim directly and failed with EACCES
(the cached pnpm.cjs has no execute bit). compile now invokes esbuild and
vite directly with && for correct failure propagation; the redundant
compile:extension/compile:appinspector-webview sub-scripts are removed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@peter-smith-phd peter-smith-phd force-pushed the integrate-resource-browsers branch from 3b6823d to f9477f6 Compare June 24, 2026 19:43
@peter-smith-phd peter-smith-phd marked this pull request as ready for review June 24, 2026 19:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant