Commit graph

6 commits

Author SHA1 Message Date
fabc782c09 clients/swift: fileprivate → internal on URLSession bridge helpers
Sessions.swift is a separate file from ForgeClient.swift but in the same
module — fileprivate blocked the cross-file call. internal (default) is
the correct visibility: same-module accessible, not part of public API.

Also dropped the unused @preconcurrency on Sessions.swift's Foundation
imports — that file doesn't reference Sendable-warning-emitting types
directly (URLSession etc), so the attribute was a no-op generating
remarks. Kept @preconcurrency on ForgeClient.swift where it actually
suppresses URL/URLSession/JSONEncoder/JSONDecoder Sendable warnings.

Caught by crafting-table queue (job 77012573) — first real dogfood of
the build farm. exit_1, log surfaced the inaccessibility error in 6s.
2026-04-29 13:44:57 -07:00
628fb09ac0 clients/swift: also patch Sessions.swift — same Apple-only URLSession.data(for:) bug 2026-04-29 13:18:07 -07:00
aeb3c30097 clients/swift: fix Linux compile — URLSession async API isn't on swift-corelibs-foundation
The audit-fix landing at 7e878e6f assumed URLSession.data(for:) and
URLSession.upload(for:fromFile:) are available on Linux Swift 5.9+.
They aren't — those are Apple-platform-only (macOS 12+, iOS 15+).
Linux's swift-corelibs-foundation only exposes the callback-style API.

Fix: private URLSession.forgeData(for:) / forgeUpload(for:fromFile:)
helpers on URLSession that bridge the callback API via
withCheckedThrowingContinuation on Linux (#if canImport(FoundationNetworking))
and forward to the native async API on Apple. 3 call sites updated.

Also added @preconcurrency to Foundation imports — URL, URLSession,
JSONEncoder, JSONDecoder are not Sendable on Linux Foundation;
@preconcurrency suppresses the spurious warnings without changing
runtime behavior.

Verified locally on Linux Swift 5.9.2 inside the crafting-table image
(7.8GB monolith with the full toolchain). The audit-fix agent that
shipped 7e878e6f was running without a swift toolchain and explicitly
flagged this verification was needed; this commit closes that.
2026-04-29 13:16:42 -07:00
0f091771d3 clients/swift: v0.2 multi-turn Session API
- Session actor with isolated mutable closed flag; nonisolated id/agent/createdAt
- withSession(opts) { s in ... } scoped helper auto-closes on success/throw
- createSession / listSessions / getSession on ForgeClient (extension)
- TurnEvent / TurnResult / SessionState / CreateSessionOptions models, snake_case CodingKeys
- TurnResult.text() helper concatenates "text" events
- Public memberwise inits on TurnEvent / TurnResult / SessionState (audit lesson)
- Session.description redacts client (no bearer leak via String(describing:))
- Session id URL-encoded with strict RFC 3986 unreserved set (audit lesson)
- Tests/SessionTests.swift: 13 tests covering create/turn/close-idempotent/
  withSession-on-success/withSession-on-throw/turn-after-close/text-concat/
  list/get/cross-token-404/redaction/v0.1-regression/empty-prompt
- README "Multi-turn / Sessions (v0.2)" section, withSession shown first

v0.1 surface unchanged; v0.2 is purely additive.

Spec: memory/spec-clawdforge-v0.2.md
Server core: 940861f
2026-04-29 07:00:56 -07:00
7e878e6f45 clients/swift: apply audit findings — multipart fix + token redaction (e4e8192 → HEAD)
P1 (release blocker):
- multipart now RFC 7578 compliant (was injecting bare LF before file
  content via Swift """...""" multi-line literals; corrupted binary
  uploads — PNG/PDF/JPEG). Body now built via explicit "\r\n"
  concatenation so every byte on the wire is auditable.

P2:
- CustomStringConvertible redacts token on ForgeClient + AppToken
  (default mirror was leaking plaintext via print / String(reflecting:)
  / SwiftUI string interpolation).
- revokeToken now pre-validates name against ^[a-z0-9_-]{1,64}$ and
  rejects path-traversal sequences with ForgeError.invalidArgument
  before percent-encoding (urlPathAllowed left /, +, ;, =, ,, @
  unescaped).
- baseURL with non-empty path/query/fragment rejected at construct.
  init is now `throws` — host-only URLs only, since the SDK builds
  request URLs by string concatenation.

P3:
- Fixed misleading "custom encoding" comment on RunRequest (it's just
  Optional + JSONEncoder default behavior).
- public init on RunFailure (was decode-only).
- Task.checkCancellation() inside the multipart chunk loop — multi-GB
  uploads now abort promptly when the parent Task is cancelled.
- 0o600 perms on the staged temp upload file (was inheriting umask,
  typically 0o644 — unwanted in multi-tenant /tmp).
- Documented JSONValue.number Double precision limit (loses precision
  for ints > 2^53).

Tests:
- testMultipartIsCRLFCompliant: writes a PNG-signature payload, scans
  the captured body for the `\r\n\n` bare-LF pattern AND verifies the
  bytes after `Content-Type: image/png\r\n\r\n` match the payload
  exactly.
- testForgeClientDescriptionRedactsToken
- testAppTokenDescriptionRedactsToken (covers both nil and non-nil
  token cases)
- testRevokeTokenRejectsTraversalName: foo/../bar, FOO, spaces, +, ;,
  =, @, 65-char names, empty
- testBaseURLWithPathRejected: /api, /v1, ?query, #fragment; host-only
  variants still accepted
- testRunFailurePublicInit
- testTempFilePerms: scans /tmp during the in-flight upload to verify
  the staged clawdforge-upload-* file is 0o600
- Existing tests updated for the now-throwing init.

README + Examples updated for the throwing init.

Audit: memory/clawdforge-audits/swift-e4e8192.md

Note: untested locally — Swift toolchain not present in this sandbox.
Needs `swift build -c release` + `swift test` verification on a Swift
5.9+ host (macOS or Linux) before tagging the next release.
2026-04-28 23:12:17 -07:00
e4e8192d4d clients/swift: initial Swift SDK for clawdforge 2026-04-28 22:48:27 -07:00