Conversation
- Implement IrisClient for HTTP requests to iris API - Add IrisSession and IrisCookieProvider protocols and models - Provide BrowserIrisCookieProvider for cookie extraction from browsers/env - Add IrisAppBundleRepository for app bundle operations - Implement IrisSDKAppBundleRepository for API interactions - Add new commands: apps iris-list and apps create via iris API - Integrate SweetCookieKit for browser cookie access - Include comprehensive tests for iris domain and commands
- Add version option to apps:create command with default "1.0" - Extend IrisAppBundleRepository to accept versionString parameter - Update IrisSDKAppBundleRepository to build JSON:API compound document including appStoreVersions and local
- Introduce AppCreateRequest and related types to build compound request - Refactor IrisSDKAppBundleRepository to use AppCreateRequest factory - Add comprehensive tests covering encoding and included relationships
- Query both apple.com and appstoreconnect.apple.com domains - Aggregate relevant cookies into a map for cleaner lookup - Return joined cookie string only if essential myacinfo cookie exists
- introduce iris command with status and apps subcommands - add IrisStatus model and command to show cookie session info - implement IrisAppsCommand with list and create subcommands - rename and update tests to reflect iris command structure - extend IrisCookieProvider to support status resolution - enhance BrowserIrisCookieProvider to report cookie source and count
- Introduce `asc iris` namespace with `status`, `apps list`, and `apps create` commands - Use cookie-based authentication via browser extraction or `ASC_IRIS_COOKIES` env var for CI/CD - Support app creation with multi-platform and locale options - Add SweetCookieKit dependency for automatic browser cookie retrieval
- Introduce Iris section in navigation and mock data - Implement Iris status and apps list with create app support - Display session info and app details with error handling
- Introduce Iris feature with icon, title, and description in all i18n JSON files - Add Iris feature card with usage example to homepage content in all supported languages - Update sitemap lastmod dates to reflect new content addition
📝 WalkthroughWalkthroughAdds an "iris" CLI namespace with subcommands to check cookie-session status, list app bundles, and create apps. Implements cookie-based auth (browser extraction via SweetCookieKit or ASC_IRIS_COOKIES), domain models, HTTP client, repository, CLI wiring, tests, docs, and web UI pages/translation updates. Changes
Sequence Diagram(s)mermaid Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 14
🧹 Nitpick comments (9)
Tests/ASCCommandTests/Commands/Iris/IrisAppsListTests.swift (1)
30-49: Prefer semantic JSON assertions over full pretty-string equality.The current assertion is fragile to harmless output formatting/key-order changes. Parse JSON and assert fields instead.
♻️ Suggested assertion refactor
+import Foundation import Mockable import Testing `@testable` import ASCCommand `@testable` import Domain @@ - `#expect`(output == """ - { - "data" : [ - { - "affordances" : { - "listAppInfos" : "asc app-infos list --app-id app-1", - "listVersions" : "asc versions list --app-id app-1" - }, - "bundleId" : "com.example.app", - "id" : "app-1", - "name" : "My App", - "platforms" : [ - "IOS" - ], - "primaryLocale" : "en-US", - "sku" : "SKU1" - } - ] - } - """) + let bytes = try `#require`(output.data(using: .utf8)) + let root = try `#require`(JSONSerialization.jsonObject(with: bytes) as? [String: Any]) + let data = try `#require`(root["data"] as? [[String: Any]]) + `#expect`(data.count == 1) + + let first = try `#require`(data.first) + `#expect`(first["id"] as? String == "app-1") + `#expect`(first["name"] as? String == "My App") + `#expect`(first["bundleId"] as? String == "com.example.app") + `#expect`(first["sku"] as? String == "SKU1") + + let affordances = try `#require`(first["affordances"] as? [String: String]) + `#expect`(affordances["listVersions"] == "asc versions list --app-id app-1") + `#expect`(affordances["listAppInfos"] == "asc app-infos list --app-id app-1")🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Tests/ASCCommandTests/Commands/Iris/IrisAppsListTests.swift` around lines 30 - 49, Replace the fragile string-equality assertion in IrisAppsListTests.swift (the `#expect`(output == """...""") block) with semantic JSON assertions: parse output into a JSON/dictionary, assert that "data" is an array of count 1, then assert properties on data[0] (id == "app-1", bundleId == "com.example.app", name == "My App", platforms contains "IOS", primaryLocale == "en-US", sku == "SKU1") and assert affordances.listAppInfos == "asc app-infos list --app-id app-1" and affordances.listVersions == "asc versions list --app-id app-1"; this removes dependency on whitespace/order while keeping the same intent.Tests/ASCCommandTests/Commands/Iris/IrisStatusTests.swift (1)
34-44: Consider a structured assertion instead of substring matching.
contains(...)is permissive; decoding/parsing and asserting thesourcefield would reduce false positives in future output changes.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Tests/ASCCommandTests/Commands/Iris/IrisStatusTests.swift` around lines 34 - 44, Replace the brittle substring assertion with a structured decode and equality check: after executing ASCCommand.IrisStatus (created via ASCCommand.IrisStatus.parse and executed with mockCookieProvider which has resolveStatus stubbed), parse the output JSON into the Iris status model (Domain.IrisStatus or a minimal Decodable struct matching the "source" field) and assert that decoded.source == .environment (or equals "environment" if using a string-backed enum); this uses the same cmd.execute and mock setup but validates the source field directly instead of using output.contains(...).apps/asc-web/command-center/js/presentation/pages/iris.js (2)
40-49: Consider logging errors for debugging.The empty
catchblock silently swallows errors. While the UI shows a fallback "No iris session" card, logging the error would help with debugging.💡 Suggestion
- } catch { + } catch (err) { + console.warn('Iris status check failed:', err); document.getElementById('irisStatus').innerHTML = `🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/asc-web/command-center/js/presentation/pages/iris.js` around lines 40 - 49, The catch block in the iris session fetch should not be empty; change the anonymous catch to accept the error (catch (err) or catch (e)) and log the error before rendering the "No iris session" UI—e.g., use console.error or the app's logger—so in the catch handling for the code that updates document.getElementById('irisStatus').innerHTML you capture and log the exception (reference the catch block around the irisStatus update) to aid debugging while keeping the existing fallback UI.
80-82: Hardcoded create modal uses placeholder values.
showIrisCreateModal()runs a command with hardcoded"My App",com.example.app, andMYSKU. This appears intentional as a quick-start demo, but users may expect input prompts.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/asc-web/command-center/js/presentation/pages/iris.js` around lines 80 - 82, The showIrisCreateModal function currently calls runAffordance with hardcoded placeholders ("My App", com.example.app, MYSKU); change it to collect real input from the user (e.g., a small modal or prompt flow) for app name, bundle id and SKU, then build the command string using those collected values and pass that to window.runAffordance; update the function (showIrisCreateModal) and any helper that opens the modal so defaults can be provided but not hardcoded into the runAffordance invocation.Sources/Infrastructure/Iris/AppBundles/IrisCreateAppDocument.swift (2)
20-28: Inner typeDatashadowsSwift.Data.Naming the inner struct
Datashadows the Foundation/SwiftDatatype. While it works due to scoping, it can cause confusion when reading or maintaining the code. Consider renaming toRequestDataorDocumentData.♻️ Suggested rename
struct AppCreateRequest: Encodable { - let data: Data + let data: RequestData let included: [IncludedResource] - struct Data: Encodable { + struct RequestData: Encodable { let type: String let attributes: Attributes let relationships: Relationships }Then update line 126:
- data: Data( + data: RequestData(🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Sources/Infrastructure/Iris/AppBundles/IrisCreateAppDocument.swift` around lines 20 - 28, The inner struct named Data shadows Swift's Foundation.Data; rename the inner type to RequestData (or DocumentData) and update all references in this file: change AppCreateRequest's property declaration from let data: Data to let data: RequestData and rename struct Data to struct RequestData; also update any use sites that reference AppCreateRequest.Data (for example usages near where Attributes/Relationships are referenced) so they point to AppCreateRequest.RequestData to avoid the shadowing and retain correct scoping.
51-60: Attributes typed as[String: String]?limits flexibility.If the iris API ever requires non-string attribute values (e.g., booleans, numbers), this type won't accommodate them. For now this works, but consider using a more flexible type like
[String: AnyCodable]or a customAttributeValueenum if needed in the future.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Sources/Infrastructure/Iris/AppBundles/IrisCreateAppDocument.swift` around lines 51 - 60, The IncludedResource struct currently types attributes as [String: String]?, which prevents non-string values; update IncludedResource (and any usages) so attributes use a flexible encodable type (e.g., [String: AnyCodable]? or a custom AttributeValue enum that conforms to Encodable) and ensure IncludedResource remains Encodable; if you choose AnyCodable add or import the AnyCodable helper and change the attributes property, or implement AttributeValue enum (cases for string, number, bool, null, object) and update encoding/decoding logic accordingly so IncludedResource.attributes can represent numbers/booleans/objects as well as strings.Sources/Infrastructure/Iris/IrisClient.swift (3)
39-39: Force-unwrap on URL construction.Same concern as the GET method—if path contains invalid characters, this will crash.
🛡️ Suggested fix
- let url = URL(string: "\(Self.baseURL)/\(path)")! + guard let url = URL(string: "\(Self.baseURL)/\(path)") else { + throw IrisAPIError.invalidResponse + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Sources/Infrastructure/Iris/IrisClient.swift` at line 39, The URL is force-unwrapped when building let url = URL(string: "\(Self.baseURL)/\(path)")!, which can crash for invalid path characters; change this to safely construct and handle failure: build the URL using URLComponents or percent-encode the path segment (use Self.baseURL and path) and replace the force-unwrap with a guard let url = URL(...) else { return/throw an appropriate error }, so the calling HTTP method (the same function that currently declares url) returns a failure instead of crashing.
51-55: Hardcoded user-agent and CSRF token may require future updates.The Chrome version string (
146.0.0.0) and the CSRF header value ([asc-ui]) are hardcoded. If Apple's API changes its validation logic or blocks outdated user-agents, this will require code changes. Consider extracting these as configurable constants or injectable parameters for easier maintenance.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Sources/Infrastructure/Iris/IrisClient.swift` around lines 51 - 55, Extract the hardcoded user-agent and CSRF header values used when configuring the request in IrisClient.swift into configurable constants or injectable parameters so they can be updated without changing code; locate the code that calls request.setValue(..., forHTTPHeaderField: "user-agent") and request.setValue(..., forHTTPHeaderField: "x-csrf-itc") and replace the literal strings with references to a configuration (e.g., constants on IrisClient or init-injected properties like userAgent and csrfTokenValue) and provide sensible defaults while allowing overrides for tests or runtime configuration.
23-27: Force-unwraps may crash on malformed paths.If
pathcontains characters that cannot form a valid URL (e.g., unescaped special characters), the force-unwrap on line 23 or 27 will crash. Consider usingguard letwith a thrown error for safer handling.🛡️ Suggested defensive handling
- var components = URLComponents(string: "\(Self.baseURL)/\(path)")! - if !queryItems.isEmpty { - components.queryItems = queryItems - } - var request = URLRequest(url: components.url!) + guard var components = URLComponents(string: "\(Self.baseURL)/\(path)"), + let url = ({ + if !queryItems.isEmpty { components.queryItems = queryItems } + return components.url + }()) else { + throw IrisAPIError.invalidResponse + } + var request = URLRequest(url: url)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Sources/Infrastructure/Iris/IrisClient.swift` around lines 23 - 27, The code in IrisClient that builds the request using URLComponents(string: "\(Self.baseURL)/\(path)") and then force-unwraps components.url for URLRequest can crash on malformed paths; update this in the request construction (where URLComponents and URLRequest are used) to safely unwrap with guard let (or try/throw) — validate URLComponents(string:) and components.url, return or throw a descriptive error (e.g., invalidURLError) from the request-building function when unwrapping fails, and ensure callers handle that error instead of relying on force-unwraps of baseURL/path.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/asc-web/command-center/js/presentation/pages/iris.js`:
- Around line 73-74: The template is inserting unescaped user-controlled values:
in the platforms mapping and app.id; wrap each platform string and the app.id
with the HTML-escaping helper (e.g., escapeHTML) before injecting into the
template to prevent XSS—use escapeHTML(p) inside the map when creating the <span
class="platform-badge"> and use escapeHTML(app.id) for the cell-mono output (and
import/ensure escapeHTML is available in iris.js if not).
In `@apps/asc-web/shared/infrastructure/mock-data.js`:
- Around line 245-258: The mock objects irisStatus and irisApps lack the
affordances field present in real Iris responses; update the mock entries for
irisStatus and irisApps to include an affordances property that mirrors the real
CLI payload (e.g., an array of affordance objects with rel, href, method and
optional body/schema) so command-center in mock mode can see next-step commands;
modify the irisStatus data item and each irisApps.data item to include
appropriate affordances (for example affordances for refresh/status-check on
irisStatus and for update/fetch/details on irisApps) keeping existing keys (id,
name, bundleId, etc.) intact.
In `@CHANGELOG.md`:
- Around line 10-17: The [Unreleased] section currently only has "### Added";
add missing "### Changed" and "### Fixed" headings under the same [Unreleased]
block (adjacent to the existing "### Added") and include either relevant entries
or placeholder lines (e.g., "N/A" or "None") under each to satisfy the
repository's Keep a Changelog format; ensure the headings are titled exactly
"### Changed" and "### Fixed" to match guidelines and maintain the existing
markdown structure.
In `@docs/features/iris.md`:
- Around line 269-282: The code block showing the directory tree starting with
"Tests/" is missing a language identifier; update the fenced code block by
changing the opening triple backticks to include the text language identifier
(i.e., replace ``` with ```text) so the directory tree (Tests/,
DomainTests/Iris/, IrisSessionTests.swift, etc.) is rendered correctly as plain
text in docs/features/iris.md.
- Around line 39-43: Update the fenced code block showing the table beginning
with "Source Cookies" to include a language identifier such as `text` or
`plaintext` after the opening backticks so the block becomes "```text"; locate
the fenced block that contains the lines "Source Cookies", "------ -------",
"browser 5" and add the identifier to the opening fence.
- Around line 244-265: The directory tree code block that starts with "Sources/"
in the Iris feature doc needs a language identifier; update the fenced code
block from ``` to ```text so the directory listing is rendered as plain text
(i.e., change the opening fence for the block containing the "Sources/" tree to
```text).
- Around line 156-176: Add the language identifier "text" to the fenced code
block containing the ASCII architecture diagram so the diagram is marked as text
(change the opening triple-backtick to "```text"); locate the ASCII diagram
block (the diagram starting with
"┌─────────────────────────────────────────────────────────────┐" and the
triple-backtick fence) and update only the opening fence to include text to
satisfy linting and improve rendering.
In `@homepage/i18n/zh.json`:
- Line 147: Update the "description" value for the App Store Connect entry (the
"description" JSON key) to avoid the absolute phrase "无需额外设置"; instead clarify
that browser-based usage uses cookie-based auth but CI/headless environments
still require providing ASC_IRIS_COOKIES (or equivalent env var) for Iris auth;
edit the sentence to contrast local browser flow vs CI/headless flow and mention
ASC_IRIS_COOKIES explicitly.
In `@homepage/index.html`:
- Around line 343-345: Update the copy in the feature card for "Iris (Private
API)" by editing the <h3> "Iris (Private API)" card's <p
class="feature-card-description"> so it no longer claims app listing is
unavailable via the public REST API; instead state that while apps can be listed
via the public GET /v1/apps endpoint, Iris unlocks private-API flows such as
cookie-based auth/backed app creation and Iris-specific private-session
workflows. Locate the paragraph text under the "Iris (Private API)" heading and
replace the inaccurate sentence about listing apps with the corrected phrasing
describing the real Iris capabilities.
In `@Package.resolved`:
- Around line 22-30: The Package.resolved entry for the dependency with
"identity" : "sweetcookiekit" is pinned to a non-existent version "0.4.0";
update that entry to the real release "0.3.0" by changing the "version" value to
"0.3.0" and also update the "revision" field to the commit hash that corresponds
to the SweetCookieKit v0.3.0 tag (or run swift package resolve / regenerate
Package.resolved to automatically populate the correct revision). Locate the
object with identity "sweetcookiekit" and apply these changes so dependency
resolution succeeds.
In `@Sources/Domain/Iris/IrisSession.swift`:
- Around line 6-7: IrisSession currently only conforms to Sendable and
Equatable; update the public struct IrisSession to also conform to Codable and
AffordanceProviding, add a public let affordances: [Affordance] property (or the
concrete affordance type used in the repo) and implement the required
AffordanceProviding requirements so the model exposes ready-to-run CLI commands;
ensure the struct signature (IrisSession) and its initializer remain stable and
that the affordances field is populated with the appropriate command definitions
expected by the Domain (use existing Affordance/CLI helpers elsewhere as a
reference).
In `@Sources/Infrastructure/Iris/AppBundles/IrisSDKAppBundleRepository.swift`:
- Around line 15-18: The request currently hardcodes URLQueryItem(name: "limit",
value: "300") in IrisSDKAppBundleRepository's fetch method (or wherever
queryItems are assembled), which will truncate results for accounts >300 apps;
update the implementation to page through results (use the API's pagination
cursor/offset and loop/recursively fetch until no next page) or at minimum
replace the hardcoded limit with a configurable constant and add a TODO noting
full pagination is required; reference the queryItems construction in
IrisSDKAppBundleRepository (and any associated fetchAppBundles / listAppBundles
method) so you implement paginated requests and merge results before returning.
In `@Sources/Infrastructure/Iris/BrowserIrisCookieProvider.swift`:
- Around line 18-22: The cookie count in resolveStatus() is computed with
cookies.components(separatedBy: "; "), which miscounts when separators lack a
space; update resolveStatus() to robustly parse the raw cookies returned by
resolve() by splitting on ";" (not "; "), trimming whitespace from each segment
with trimmingCharacters(in: .whitespacesAndNewlines), filtering out empty
segments, and using the resulting array's count when constructing
IrisStatus(cookieCount:); reference resolveStatus(), resolve(), and IrisStatus
in your change.
In `@Tests/DomainTests/Iris/IrisStatusTests.swift`:
- Around line 13-16: The test `status from environment shows source` creates
IrisStatus(source: .environment, cookieCount: 3) but only asserts
`status.source`; add an assertion to also verify `status.cookieCount == 3` in
that test (same pattern used in the browser case) so both source and cookieCount
are validated for the environment path.
---
Nitpick comments:
In `@apps/asc-web/command-center/js/presentation/pages/iris.js`:
- Around line 40-49: The catch block in the iris session fetch should not be
empty; change the anonymous catch to accept the error (catch (err) or catch (e))
and log the error before rendering the "No iris session" UI—e.g., use
console.error or the app's logger—so in the catch handling for the code that
updates document.getElementById('irisStatus').innerHTML you capture and log the
exception (reference the catch block around the irisStatus update) to aid
debugging while keeping the existing fallback UI.
- Around line 80-82: The showIrisCreateModal function currently calls
runAffordance with hardcoded placeholders ("My App", com.example.app, MYSKU);
change it to collect real input from the user (e.g., a small modal or prompt
flow) for app name, bundle id and SKU, then build the command string using those
collected values and pass that to window.runAffordance; update the function
(showIrisCreateModal) and any helper that opens the modal so defaults can be
provided but not hardcoded into the runAffordance invocation.
In `@Sources/Infrastructure/Iris/AppBundles/IrisCreateAppDocument.swift`:
- Around line 20-28: The inner struct named Data shadows Swift's
Foundation.Data; rename the inner type to RequestData (or DocumentData) and
update all references in this file: change AppCreateRequest's property
declaration from let data: Data to let data: RequestData and rename struct Data
to struct RequestData; also update any use sites that reference
AppCreateRequest.Data (for example usages near where Attributes/Relationships
are referenced) so they point to AppCreateRequest.RequestData to avoid the
shadowing and retain correct scoping.
- Around line 51-60: The IncludedResource struct currently types attributes as
[String: String]?, which prevents non-string values; update IncludedResource
(and any usages) so attributes use a flexible encodable type (e.g., [String:
AnyCodable]? or a custom AttributeValue enum that conforms to Encodable) and
ensure IncludedResource remains Encodable; if you choose AnyCodable add or
import the AnyCodable helper and change the attributes property, or implement
AttributeValue enum (cases for string, number, bool, null, object) and update
encoding/decoding logic accordingly so IncludedResource.attributes can represent
numbers/booleans/objects as well as strings.
In `@Sources/Infrastructure/Iris/IrisClient.swift`:
- Line 39: The URL is force-unwrapped when building let url = URL(string:
"\(Self.baseURL)/\(path)")!, which can crash for invalid path characters; change
this to safely construct and handle failure: build the URL using URLComponents
or percent-encode the path segment (use Self.baseURL and path) and replace the
force-unwrap with a guard let url = URL(...) else { return/throw an appropriate
error }, so the calling HTTP method (the same function that currently declares
url) returns a failure instead of crashing.
- Around line 51-55: Extract the hardcoded user-agent and CSRF header values
used when configuring the request in IrisClient.swift into configurable
constants or injectable parameters so they can be updated without changing code;
locate the code that calls request.setValue(..., forHTTPHeaderField:
"user-agent") and request.setValue(..., forHTTPHeaderField: "x-csrf-itc") and
replace the literal strings with references to a configuration (e.g., constants
on IrisClient or init-injected properties like userAgent and csrfTokenValue) and
provide sensible defaults while allowing overrides for tests or runtime
configuration.
- Around line 23-27: The code in IrisClient that builds the request using
URLComponents(string: "\(Self.baseURL)/\(path)") and then force-unwraps
components.url for URLRequest can crash on malformed paths; update this in the
request construction (where URLComponents and URLRequest are used) to safely
unwrap with guard let (or try/throw) — validate URLComponents(string:) and
components.url, return or throw a descriptive error (e.g., invalidURLError) from
the request-building function when unwrapping fails, and ensure callers handle
that error instead of relying on force-unwraps of baseURL/path.
In `@Tests/ASCCommandTests/Commands/Iris/IrisAppsListTests.swift`:
- Around line 30-49: Replace the fragile string-equality assertion in
IrisAppsListTests.swift (the `#expect`(output == """...""") block) with semantic
JSON assertions: parse output into a JSON/dictionary, assert that "data" is an
array of count 1, then assert properties on data[0] (id == "app-1", bundleId ==
"com.example.app", name == "My App", platforms contains "IOS", primaryLocale ==
"en-US", sku == "SKU1") and assert affordances.listAppInfos == "asc app-infos
list --app-id app-1" and affordances.listVersions == "asc versions list --app-id
app-1"; this removes dependency on whitespace/order while keeping the same
intent.
In `@Tests/ASCCommandTests/Commands/Iris/IrisStatusTests.swift`:
- Around line 34-44: Replace the brittle substring assertion with a structured
decode and equality check: after executing ASCCommand.IrisStatus (created via
ASCCommand.IrisStatus.parse and executed with mockCookieProvider which has
resolveStatus stubbed), parse the output JSON into the Iris status model
(Domain.IrisStatus or a minimal Decodable struct matching the "source" field)
and assert that decoded.source == .environment (or equals "environment" if using
a string-backed enum); this uses the same cmd.execute and mock setup but
validates the source field directly instead of using output.contains(...).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: a830acb6-01d0-4014-a37f-6d68e2a7e616
📒 Files selected for processing (62)
CHANGELOG.mdPackage.resolvedPackage.swiftREADME.mdSources/ASCCommand/ASC.swiftSources/ASCCommand/ClientProvider.swiftSources/ASCCommand/Commands/Iris/IrisAppsCommand.swiftSources/ASCCommand/Commands/Iris/IrisAppsCreate.swiftSources/ASCCommand/Commands/Iris/IrisAppsList.swiftSources/ASCCommand/Commands/Iris/IrisCommand.swiftSources/ASCCommand/Commands/Iris/IrisStatus.swiftSources/Domain/Iris/AppBundles/AppBundle.swiftSources/Domain/Iris/AppBundles/IrisAppBundleRepository.swiftSources/Domain/Iris/IrisCookieProvider.swiftSources/Domain/Iris/IrisSession.swiftSources/Domain/Iris/IrisStatus.swiftSources/Infrastructure/Client/ClientFactory.swiftSources/Infrastructure/Iris/AppBundles/IrisCreateAppDocument.swiftSources/Infrastructure/Iris/AppBundles/IrisSDKAppBundleRepository.swiftSources/Infrastructure/Iris/BrowserIrisCookieProvider.swiftSources/Infrastructure/Iris/IrisClient.swiftTests/ASCCommandTests/Commands/Iris/IrisAppsCreateTests.swiftTests/ASCCommandTests/Commands/Iris/IrisAppsListTests.swiftTests/ASCCommandTests/Commands/Iris/IrisStatusTests.swiftTests/DomainTests/Iris/AppBundles/AppBundleTests.swiftTests/DomainTests/Iris/IrisSessionTests.swiftTests/DomainTests/Iris/IrisStatusTests.swiftTests/DomainTests/TestHelpers/MockRepositoryFactory.swiftTests/InfrastructureTests/Iris/IrisCreateAppDocumentTests.swiftapps/asc-web/ARCHITECTURE.mdapps/asc-web/command-center/index.htmlapps/asc-web/command-center/js/presentation/navigation.jsapps/asc-web/command-center/js/presentation/pages/iris.jsapps/asc-web/console/js/presentation/nav-data.jsapps/asc-web/shared/infrastructure/mock-data.jsdocs/features/iris.mdhomepage/es/index.htmlhomepage/fr/index.htmlhomepage/i18n/en.jsonhomepage/i18n/es.jsonhomepage/i18n/fr.jsonhomepage/i18n/it.jsonhomepage/i18n/ja.jsonhomepage/i18n/ko.jsonhomepage/i18n/pt.jsonhomepage/i18n/ru.jsonhomepage/i18n/th.jsonhomepage/i18n/vi.jsonhomepage/i18n/zh-TW.jsonhomepage/i18n/zh.jsonhomepage/index.htmlhomepage/it/index.htmlhomepage/ja/index.htmlhomepage/ko/index.htmlhomepage/pt/index.htmlhomepage/ru/index.htmlhomepage/sitemap.xmlhomepage/template.htmlhomepage/th/index.htmlhomepage/vi/index.htmlhomepage/zh-TW/index.htmlhomepage/zh/index.html
| <td>${(app.platforms || []).map(p => `<span class="platform-badge">${p}</span>`).join(' ')}</td> | ||
| <td class="cell-mono">${app.id}</td> |
There was a problem hiding this comment.
Escape user-controlled values to prevent XSS.
app.platforms elements and app.id are rendered without escapeHTML(). If these values contain HTML characters, they could be exploited.
🔒 Proposed fix
- <td>${(app.platforms || []).map(p => `<span class="platform-badge">${p}</span>`).join(' ')}</td>
- <td class="cell-mono">${app.id}</td>
+ <td>${(app.platforms || []).map(p => `<span class="platform-badge">${escapeHTML(p)}</span>`).join(' ')}</td>
+ <td class="cell-mono">${escapeHTML(app.id)}</td>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/asc-web/command-center/js/presentation/pages/iris.js` around lines 73 -
74, The template is inserting unescaped user-controlled values: in the platforms
mapping and app.id; wrap each platform string and the app.id with the
HTML-escaping helper (e.g., escapeHTML) before injecting into the template to
prevent XSS—use escapeHTML(p) inside the map when creating the <span
class="platform-badge"> and use escapeHTML(app.id) for the cell-mono output (and
import/ensure escapeHTML is available in iris.js if not).
| // ===== Iris Status ===== | ||
| irisStatus: { | ||
| data: [ | ||
| { source: 'browser', cookieCount: 7 }, | ||
| ], | ||
| }, | ||
|
|
||
| // ===== Iris App Bundles ===== | ||
| irisApps: { | ||
| data: [ | ||
| { id: '6449071230', name: 'PhotoSync Pro', bundleId: 'com.example.photosync', sku: 'PHOTOSYNC2024', primaryLocale: 'en-US', platforms: ['IOS'] }, | ||
| { id: '6449071231', name: 'TaskFlow', bundleId: 'com.example.taskflow', sku: 'TASKFLOW2024', primaryLocale: 'en-US', platforms: ['IOS', 'MAC_OS'] }, | ||
| ], | ||
| }, |
There was a problem hiding this comment.
Add Iris affordances to mocks to mirror real CLI payloads.
irisStatus and irisApps are missing affordances, while real Iris responses expose next-step commands. This can drift command-center behavior in mock mode.
💡 Proposed mock-shape alignment
irisStatus: {
data: [
- { source: 'browser', cookieCount: 7 },
+ {
+ source: 'browser',
+ cookieCount: 7,
+ affordances: {
+ listApps: 'asc iris apps list',
+ createApp: 'asc iris apps create --name <name> --bundle-id <id> --sku <sku>',
+ },
+ },
],
},
// ===== Iris App Bundles =====
irisApps: {
data: [
- { id: '6449071230', name: 'PhotoSync Pro', bundleId: 'com.example.photosync', sku: 'PHOTOSYNC2024', primaryLocale: 'en-US', platforms: ['IOS'] },
- { id: '6449071231', name: 'TaskFlow', bundleId: 'com.example.taskflow', sku: 'TASKFLOW2024', primaryLocale: 'en-US', platforms: ['IOS', 'MAC_OS'] },
+ {
+ id: '6449071230',
+ name: 'PhotoSync Pro',
+ bundleId: 'com.example.photosync',
+ sku: 'PHOTOSYNC2024',
+ primaryLocale: 'en-US',
+ platforms: ['IOS'],
+ affordances: {
+ listAppInfos: 'asc app-infos list --app-id 6449071230',
+ listVersions: 'asc versions list --app-id 6449071230',
+ },
+ },
+ {
+ id: '6449071231',
+ name: 'TaskFlow',
+ bundleId: 'com.example.taskflow',
+ sku: 'TASKFLOW2024',
+ primaryLocale: 'en-US',
+ platforms: ['IOS', 'MAC_OS'],
+ affordances: {
+ listAppInfos: 'asc app-infos list --app-id 6449071231',
+ listVersions: 'asc versions list --app-id 6449071231',
+ },
+ },
],
},🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/asc-web/shared/infrastructure/mock-data.js` around lines 245 - 258, The
mock objects irisStatus and irisApps lack the affordances field present in real
Iris responses; update the mock entries for irisStatus and irisApps to include
an affordances property that mirrors the real CLI payload (e.g., an array of
affordance objects with rel, href, method and optional body/schema) so
command-center in mock mode can see next-step commands; modify the irisStatus
data item and each irisApps.data item to include appropriate affordances (for
example affordances for refresh/status-check on irisStatus and for
update/fetch/details on irisApps) keeping existing keys (id, name, bundleId,
etc.) intact.
| ### Added | ||
| - **Iris private API support** — new `asc iris` command namespace for cookie-based App Store Connect private API access | ||
| - **`asc iris status`** — check iris cookie session availability (browser or environment source) | ||
| - **`asc iris apps list`** — list apps via iris `/v1/appBundles` endpoint | ||
| - **`asc iris apps create`** — create a new app with initial version, supports multi-platform (`--platforms IOS MAC_OS`) | ||
| - **Cookie-based authentication** — auto-extracts `myacinfo` and session cookies from Chrome/Safari/Firefox via SweetCookieKit, with `ASC_IRIS_COOKIES` env var fallback for CI/CD | ||
| - **SweetCookieKit dependency** — browser cookie extraction for iris authentication | ||
|
|
There was a problem hiding this comment.
Add missing Changed and Fixed sections under [Unreleased].
[Unreleased] currently includes only ### Added; the repo guideline requires Added, Changed, and Fixed categories.
📝 Suggested patch
## [Unreleased]
### Added
- **Iris private API support** — new `asc iris` command namespace for cookie-based App Store Connect private API access
- **`asc iris status`** — check iris cookie session availability (browser or environment source)
- **`asc iris apps list`** — list apps via iris `/v1/appBundles` endpoint
- **`asc iris apps create`** — create a new app with initial version, supports multi-platform (`--platforms IOS MAC_OS`)
- **Cookie-based authentication** — auto-extracts `myacinfo` and session cookies from Chrome/Safari/Firefox via SweetCookieKit, with `ASC_IRIS_COOKIES` env var fallback for CI/CD
- **SweetCookieKit dependency** — browser cookie extraction for iris authentication
+
+### Changed
+- None.
+
+### Fixed
+- None.As per coding guidelines CHANGELOG.md: "Add changelog entries under [Unreleased] section using Keep a Changelog format with categories: Added, Changed, Fixed".
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ### Added | |
| - **Iris private API support** — new `asc iris` command namespace for cookie-based App Store Connect private API access | |
| - **`asc iris status`** — check iris cookie session availability (browser or environment source) | |
| - **`asc iris apps list`** — list apps via iris `/v1/appBundles` endpoint | |
| - **`asc iris apps create`** — create a new app with initial version, supports multi-platform (`--platforms IOS MAC_OS`) | |
| - **Cookie-based authentication** — auto-extracts `myacinfo` and session cookies from Chrome/Safari/Firefox via SweetCookieKit, with `ASC_IRIS_COOKIES` env var fallback for CI/CD | |
| - **SweetCookieKit dependency** — browser cookie extraction for iris authentication | |
| ### Added | |
| - **Iris private API support** — new `asc iris` command namespace for cookie-based App Store Connect private API access | |
| - **`asc iris status`** — check iris cookie session availability (browser or environment source) | |
| - **`asc iris apps list`** — list apps via iris `/v1/appBundles` endpoint | |
| - **`asc iris apps create`** — create a new app with initial version, supports multi-platform (`--platforms IOS MAC_OS`) | |
| - **Cookie-based authentication** — auto-extracts `myacinfo` and session cookies from Chrome/Safari/Firefox via SweetCookieKit, with `ASC_IRIS_COOKIES` env var fallback for CI/CD | |
| - **SweetCookieKit dependency** — browser cookie extraction for iris authentication | |
| ### Changed | |
| - None. | |
| ### Fixed | |
| - None. |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@CHANGELOG.md` around lines 10 - 17, The [Unreleased] section currently only
has "### Added"; add missing "### Changed" and "### Fixed" headings under the
same [Unreleased] block (adjacent to the existing "### Added") and include
either relevant entries or placeholder lines (e.g., "N/A" or "None") under each
to satisfy the repository's Keep a Changelog format; ensure the headings are
titled exactly "### Changed" and "### Fixed" to match guidelines and maintain
the existing markdown structure.
| ``` | ||
| Source Cookies | ||
| ------ ------- | ||
| browser 5 | ||
| ``` |
There was a problem hiding this comment.
Add language identifier to fenced code block.
The table output example is missing a language identifier. Use text or plaintext for non-code output.
Proposed fix
-```
+```text
Source Cookies
------ -------
browser 5</details>
<details>
<summary>🧰 Tools</summary>
<details>
<summary>🪛 markdownlint-cli2 (0.21.0)</summary>
[warning] 39-39: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
</details>
</details>
<details>
<summary>🤖 Prompt for AI Agents</summary>
Verify each finding against the current code and only fix it if needed.
In @docs/features/iris.md around lines 39 - 43, Update the fenced code block
showing the table beginning with "Source Cookies" to include a language
identifier such as text or plaintext after the opening backticks so the
block becomes "```text"; locate the fenced block that contains the lines "Source
Cookies", "------ -------", "browser 5" and add the identifier to the opening
fence.
</details>
<!-- fingerprinting:phantom:poseidon:ocelot -->
<!-- This is an auto-generated comment by CodeRabbit -->
| ``` | ||
| ┌─────────────────────────────────────────────────────────────┐ | ||
| │ ASCCommand │ | ||
| │ ┌──────────┐ ┌────────────┐ ┌────────────────┐ │ | ||
| │ │IrisStatus │ │IrisAppsList│ │IrisAppsCreate │ │ | ||
| │ └─────┬────┘ └─────┬──────┘ └───────┬────────┘ │ | ||
| │ │ │ │ │ | ||
| │ └─────────────┴──────────────────┘ │ | ||
| │ │ ClientProvider │ | ||
| ├──────────────────────┼──────────────────────────────────────┤ | ||
| │ Infrastructure │ │ | ||
| │ ┌───────────────────┴──────────┐ ┌──────────────────────┐ │ | ||
| │ │ BrowserIrisCookieProvider │ │ IrisSDKAppBundleRepo │ │ | ||
| │ │ (SweetCookieKit) │ │ (IrisClient → HTTP) │ │ | ||
| │ └──────────────────────────────┘ └──────────────────────┘ │ | ||
| ├─────────────────────────────────────────────────────────────┤ | ||
| │ Domain │ | ||
| │ IrisSession · IrisStatus · IrisCookieProvider(@Mockable) │ | ||
| │ AppBundle · IrisAppBundleRepository(@Mockable) │ | ||
| └─────────────────────────────────────────────────────────────┘ | ||
| ``` |
There was a problem hiding this comment.
Add language identifier to architecture diagram code block.
Use text for ASCII diagrams to satisfy linting and improve rendering consistency.
Proposed fix
-```
+```text
┌─────────────────────────────────────────────────────────────┐
│ ASCCommand │🧰 Tools
🪛 markdownlint-cli2 (0.21.0)
[warning] 156-156: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/features/iris.md` around lines 156 - 176, Add the language identifier
"text" to the fenced code block containing the ASCII architecture diagram so the
diagram is marked as text (change the opening triple-backtick to "```text");
locate the ASCII diagram block (the diagram starting with
"┌─────────────────────────────────────────────────────────────┐" and the
triple-backtick fence) and update only the opening fence to include text to
satisfy linting and improve rendering.
| { | ||
| "identity" : "sweetcookiekit", | ||
| "kind" : "remoteSourceControl", | ||
| "location" : "https://github.com/steipete/SweetCookieKit.git", | ||
| "state" : { | ||
| "revision" : "4d5b71ffbb296937dc5ee8472f64721bca771cf0", | ||
| "version" : "0.4.0" | ||
| } | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
What is the latest release of SweetCookieKit (github.com/steipete/SweetCookieKit), and are there any published security advisories affecting version 0.4.0?
💡 Result:
The latest release of SweetCookieKit (github.com/steipete/SweetCookieKit) is version 0.3.0, released on 2026-01-20. There are no published security advisories for the project, including for version 0.4.0 (which does not appear to exist). The GitHub security overview states there are no published advisories, and the repo has no SECURITY.md file.
Citations:
- 1: https://github.com/steipete/SweetCookieKit/releases
- 2: https://github.com/steipete/SweetCookieKit
- 3: https://github.com/steipete/SweetCookieKit/security
Update SweetCookieKit pin to version 0.3.0; version 0.4.0 does not exist.
The pinned version 0.4.0 does not exist—the latest release is 0.3.0 (2026-01-20). Update the pin accordingly to resolve dependency resolution failures. No known security advisories affect this dependency.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Package.resolved` around lines 22 - 30, The Package.resolved entry for the
dependency with "identity" : "sweetcookiekit" is pinned to a non-existent
version "0.4.0"; update that entry to the real release "0.3.0" by changing the
"version" value to "0.3.0" and also update the "revision" field to the commit
hash that corresponds to the SweetCookieKit v0.3.0 tag (or run swift package
resolve / regenerate Package.resolved to automatically populate the correct
revision). Locate the object with identity "sweetcookiekit" and apply these
changes so dependency resolution succeeds.
| public struct IrisSession: Sendable, Equatable { | ||
| public let cookies: String |
There was a problem hiding this comment.
Add required Domain-model conformances before merge.
Line 6 defines IrisSession without Codable, and the model also lacks AffordanceProviding + affordances, which breaks the Domain model contract used across this repo.
As per coding guidelines: "All Domain layer models are public struct + Sendable + Equatable + Codable" and "All domain models must implement AffordanceProviding protocol with an affordances field containing ready-to-run CLI commands".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Sources/Domain/Iris/IrisSession.swift` around lines 6 - 7, IrisSession
currently only conforms to Sendable and Equatable; update the public struct
IrisSession to also conform to Codable and AffordanceProviding, add a public let
affordances: [Affordance] property (or the concrete affordance type used in the
repo) and implement the required AffordanceProviding requirements so the model
exposes ready-to-run CLI commands; ensure the struct signature (IrisSession) and
its initializer remain stable and that the affordances field is populated with
the appropriate command definitions expected by the Domain (use existing
Affordance/CLI helpers elsewhere as a reference).
| public func resolveStatus() throws -> IrisStatus { | ||
| let (cookies, source) = try resolve() | ||
| let count = cookies.components(separatedBy: "; ").count | ||
| return IrisStatus(source: source, cookieCount: count) | ||
| } |
There was a problem hiding this comment.
Cookie count may be inaccurate for environment variable input.
cookieCount is calculated by splitting on "; ", but the environment variable format isn't validated. If users provide cookies with different separators (e.g., ";" without space), the count will be incorrect.
💡 Suggested improvement
public func resolveStatus() throws -> IrisStatus {
let (cookies, source) = try resolve()
- let count = cookies.components(separatedBy: "; ").count
+ // Handle both "; " and ";" separators for robustness
+ let count = cookies.split { $0 == ";" }.count
return IrisStatus(source: source, cookieCount: count)
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Sources/Infrastructure/Iris/BrowserIrisCookieProvider.swift` around lines 18
- 22, The cookie count in resolveStatus() is computed with
cookies.components(separatedBy: "; "), which miscounts when separators lack a
space; update resolveStatus() to robustly parse the raw cookies returned by
resolve() by splitting on ";" (not "; "), trimming whitespace from each segment
with trimmingCharacters(in: .whitespacesAndNewlines), filtering out empty
segments, and using the resulting array's count when constructing
IrisStatus(cookieCount:); reference resolveStatus(), resolve(), and IrisStatus
in your change.
| @Test func `status from environment shows source`() { | ||
| let status = IrisStatus(source: .environment, cookieCount: 3) | ||
| #expect(status.source == .environment) | ||
| } |
There was a problem hiding this comment.
Also assert cookieCount for the environment-source path.
The environment case currently validates only source; adding cookieCount keeps parity with the browser case and closes a small regression gap.
✅ Suggested test assertion
`@Test` func `status from environment shows source`() {
let status = IrisStatus(source: .environment, cookieCount: 3)
`#expect`(status.source == .environment)
+ `#expect`(status.cookieCount == 3)
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Tests/DomainTests/Iris/IrisStatusTests.swift` around lines 13 - 16, The test
`status from environment shows source` creates IrisStatus(source: .environment,
cookieCount: 3) but only asserts `status.source`; add an assertion to also
verify `status.cookieCount == 3` in that test (same pattern used in the browser
case) so both source and cookieCount are validated for the environment path.
Welcome to Codecov 🎉Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests. Thanks for integrating Codecov - We've got you covered ☂️ |
|
Create apps directly from the terminal #7 |
- Introduce feature.yml with structured prompts for proposing enhancements - Add bug.yml to standardize bug report submission and reproduction steps
ci(workflow): exclude Iris modules from coverage ci(codecov): add Iris files to ignore list in coverage configuration
…e decoding - Validate IrisAPIError descriptions for various cases - Verify IrisCookieError descriptive messages - Cover decoding of app bundle list, single bundle, and nil attributes - Test mapping from API resources to AppBundle model with fallbacks
There was a problem hiding this comment.
♻️ Duplicate comments (1)
Sources/Infrastructure/Iris/AppBundles/IrisSDKAppBundleRepository.swift (1)
15-18:⚠️ Potential issue | 🟠 MajorPagination is still missing for app bundle listing.
Using a fixed
limit=300can silently truncate results for larger accounts. Please paginate (or at minimum surface a clear truncation warning) before release.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Sources/Infrastructure/Iris/AppBundles/IrisSDKAppBundleRepository.swift` around lines 15 - 18, The app bundle list currently uses a fixed URLQueryItem(limit= "300") in IrisSDKAppBundleRepository which can truncate results; change the listing logic in IrisSDKAppBundleRepository (the method building those URLQueryItem entries and parsing the response) to perform proper pagination instead of a single fixed-limit request: detect the API's pagination mechanism (offset/page or cursor/next token) from the response, loop fetching subsequent pages (appending URLQueryItem like "page"/"offset" or "cursor"/"next") until no more results, and merge results into the single returned collection; if the backend cannot provide pagination, replace the fixed limit with a warning/log that results may be truncated and surface the fact to callers (e.g., via an error/flag) rather than silently returning only 300 items.
🧹 Nitpick comments (2)
Tests/InfrastructureTests/Iris/IrisResponseDecodingTests.swift (2)
90-135: Prefer behavior-level tests over direct mapper internals.These cases are centered on
mapToAppBundleinternals rather than user-observable repository outcomes. Consider asserting throughlistAppBundles/createAppbehavior with fixtures so refactors of internal mapping don’t force unnecessary test churn.As per coding guidelines, "Test cases must reflect the user's mental model — describe what the user sees and expects, not internal implementation details".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Tests/InfrastructureTests/Iris/IrisResponseDecodingTests.swift` around lines 90 - 135, Replace the fragile, implementation-focused tests that call IrisSDKAppBundleRepository.mapToAppBundle directly with behavior-level tests that exercise repository API surface (e.g., call IrisSDKAppBundleRepository.listAppBundles or createApp using test fixtures) and assert the user-observable results (returned AppBundle fields) instead; update tests that currently reference mapToAppBundle, makeResource, and direct field assertions to create fixtures covering nil name/platforms/primaryLocale scenarios, invoke the repository method a consumer would use, and assert the resulting AppBundle properties so refactors to the mapper implementation won’t break the tests.
1-7: Align this AppBundles suite path with the Domain hierarchy.Since this suite validates AppBundles decoding/mapping, consider placing it under an
Iris/AppBundlesmirrored test path for consistency with the required structure.As per coding guidelines, "Infrastructure and test folder structures must mirror the Domain layer structure exactly".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Tests/InfrastructureTests/Iris/IrisResponseDecodingTests.swift` around lines 1 - 7, This test suite (IrisResponseDecodingTests) must be relocated into the test hierarchy that mirrors the Domain layer's Iris/AppBundles structure and renamed/organized to reflect that it's validating AppBundles decoding; move the test into the Iris -> AppBundles test group and rename the struct (for example to AppBundlesResponseDecodingTests or IrisAppBundlesResponseDecodingTests) and update any references/imports so the suite and file path match the Domain Iris/AppBundles namespace exactly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@Sources/Infrastructure/Iris/AppBundles/IrisSDKAppBundleRepository.swift`:
- Around line 15-18: The app bundle list currently uses a fixed
URLQueryItem(limit= "300") in IrisSDKAppBundleRepository which can truncate
results; change the listing logic in IrisSDKAppBundleRepository (the method
building those URLQueryItem entries and parsing the response) to perform proper
pagination instead of a single fixed-limit request: detect the API's pagination
mechanism (offset/page or cursor/next token) from the response, loop fetching
subsequent pages (appending URLQueryItem like "page"/"offset" or
"cursor"/"next") until no more results, and merge results into the single
returned collection; if the backend cannot provide pagination, replace the fixed
limit with a warning/log that results may be truncated and surface the fact to
callers (e.g., via an error/flag) rather than silently returning only 300 items.
---
Nitpick comments:
In `@Tests/InfrastructureTests/Iris/IrisResponseDecodingTests.swift`:
- Around line 90-135: Replace the fragile, implementation-focused tests that
call IrisSDKAppBundleRepository.mapToAppBundle directly with behavior-level
tests that exercise repository API surface (e.g., call
IrisSDKAppBundleRepository.listAppBundles or createApp using test fixtures) and
assert the user-observable results (returned AppBundle fields) instead; update
tests that currently reference mapToAppBundle, makeResource, and direct field
assertions to create fixtures covering nil name/platforms/primaryLocale
scenarios, invoke the repository method a consumer would use, and assert the
resulting AppBundle properties so refactors to the mapper implementation won’t
break the tests.
- Around line 1-7: This test suite (IrisResponseDecodingTests) must be relocated
into the test hierarchy that mirrors the Domain layer's Iris/AppBundles
structure and renamed/organized to reflect that it's validating AppBundles
decoding; move the test into the Iris -> AppBundles test group and rename the
struct (for example to AppBundlesResponseDecodingTests or
IrisAppBundlesResponseDecodingTests) and update any references/imports so the
suite and file path match the Domain Iris/AppBundles namespace exactly.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: bb6b2e75-8ec8-40d2-8c0f-384248e28293
📒 Files selected for processing (8)
.github/ISSUE_TEMPLATE/bug.yml.github/ISSUE_TEMPLATE/feature.yml.github/workflows/ci.ymlSources/Infrastructure/Iris/AppBundles/IrisSDKAppBundleRepository.swiftTests/InfrastructureTests/Iris/IrisAPIErrorTests.swiftTests/InfrastructureTests/Iris/IrisCookieErrorTests.swiftTests/InfrastructureTests/Iris/IrisResponseDecodingTests.swiftcodecov.yml
✅ Files skipped from review due to trivial changes (4)
- codecov.yml
- .github/ISSUE_TEMPLATE/bug.yml
- .github/ISSUE_TEMPLATE/feature.yml
- .github/workflows/ci.yml
This pull request introduces a major new feature: support for the App Store Connect "iris" private API, enabling cookie-based authentication and new commands for app management. It adds a new
asc iriscommand namespace, implements cookie extraction from browsers or environment variables, and provides the ability to list and create apps via the private API. The implementation includes new domain models, repositories, infrastructure, and documentation.Iris Private API Support
asc iriscommand namespace with subcommands for interacting with the App Store Connect private API using browser cookies (IrisCommand,IrisStatus,IrisAppsCommand,IrisAppsList,IrisAppsCreate) [1] [2] [3] [4] [5].SweetCookieKitdependency, with CI/CD fallback using theASC_IRIS_COOKIESenvironment variable [1] [2] [3] [4].Domain and Infrastructure Additions
AppBundle,IrisSession,IrisStatus,IrisCookieProvider, andIrisAppBundleRepository[1] [2] [3] [4] [5].IrisSDKAppBundleRepository,BrowserIrisCookieProvider,AppCreateRequest) [1] [2].Client and Command Integration
IrisCommandand provided access to iris repositories and cookie providers viaClientProvider[1] [2].Documentation Updates
README.mdandCHANGELOG.mdto document the new iris features, usage examples, and authentication flow [1] [2] [3] [4].Iris Private API Command Support
asc iriscommand namespace with subcommands for checking session status, listing apps, and creating apps using the iris private API. [1] [2] [3] [4] [5]SweetCookieKitdependency, and fallback to environment variable for CI/CD. [1] [2] [3] [4]Domain and Infrastructure Enhancements
AppBundle,IrisSession,IrisStatus,IrisCookieProvider,IrisAppBundleRepository). [1] [2] [3] [4] [5]Integration and Registration
ClientProviderand command entry points. [1] [2]Documentation
README.mdandCHANGELOG.mdwith iris feature documentation, usage instructions, and authentication details. [1] [2] [3] [4]Summary by CodeRabbit
New Features
asc iris) withstatus,apps list, andapps create(multi-platform app creation).Web
Documentation