Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/scripts/central_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def check_ci(info):


def read_bundle(path, version):
prefix = f'com/qoder/qca-sdk/{version}/qca-sdk-{version}'
prefix = f'com/qoder/sdk/qoder-cloud-agents-sdk/{version}/qoder-cloud-agents-sdk-{version}'
primary = {prefix + suffix for suffix in ('.pom', '.jar', '-sources.jar', '-javadoc.jar')}
required = primary | {name + '.asc' for name in primary}
allowed = required | {name + '.' + algo for name in required for algo in ('md5', 'sha1', 'sha256', 'sha512')}
Expand Down Expand Up @@ -161,11 +161,11 @@ def seal(source, candidate, output):
contents = read_bundle(bundle, info['version'])
checks = json.loads((candidate / 'package-check.json').read_text())['sha256']
for local, expected in checks.items():
filename = f"qca-sdk-{info['version']}.pom" if local == 'pom.xml' else Path(local).name
path = f"com/qoder/qca-sdk/{info['version']}/{filename}"
filename = f"qoder-cloud-agents-sdk-{info['version']}.pom" if local == 'pom.xml' else Path(local).name
path = f"com/qoder/sdk/qoder-cloud-agents-sdk/{info['version']}/{filename}"
require(hashlib.sha256(contents[path]).hexdigest() == expected,
'Signed bundle differs from reviewed build: ' + filename)
expected_files = {'pom.xml'} | {f"target/qca-sdk-{info['version']}{suffix}.jar"
expected_files = {'pom.xml'} | {f"target/qoder-cloud-agents-sdk-{info['version']}{suffix}.jar"
for suffix in ('', '-sources', '-javadoc')}
require(set(checks) == expected_files, 'Incomplete candidate digests')
info.update(run_id=run_id(os.environ['GITHUB_RUN_ID']), workflow_sha=os.environ['GITHUB_SHA'],
Expand Down Expand Up @@ -247,7 +247,7 @@ def portal_request(path, data=b'', content_type=None):


def deployment_name(info):
return f"qca-sdk-{info['version']}-{info['source_sha']}-{info['batch_id']}-{info['bundle_sha256']}"
return f"qoder-cloud-agents-sdk-{info['version']}-{info['source_sha']}-{info['batch_id']}-{info['bundle_sha256']}"


def upload(output):
Expand Down Expand Up @@ -277,7 +277,7 @@ def publish(output):
status = json.loads(portal_request('/status?' + urllib.parse.urlencode(dict(id=identifier))))
require(status.get('deploymentId') == identifier
and status.get('deploymentName') == deployment_name(info), 'Central deployment identity differs')
expected = f"pkg:maven/com.qoder/qca-sdk@{info['version']}"
expected = f"pkg:maven/com.qoder.sdk/qoder-cloud-agents-sdk@{info['version']}"
require(not status.get('purls') or status['purls'] == [expected], 'Unexpected Central coordinates')
state = status.get('deploymentState')
require(state in ('PENDING', 'VALIDATING', 'VALIDATED', 'PUBLISHING', 'PUBLISHED', 'FAILED'),
Expand Down
6 changes: 4 additions & 2 deletions .github/scripts/check_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ def check(root):
pom = root / "pom.xml"
xml = ET.parse(pom).getroot()
ns = {"m": "http://maven.apache.org/POM/4.0.0"}
group = xml.findtext("m:groupId", namespaces=ns)
artifact = xml.findtext("m:artifactId", namespaces=ns)
version = xml.findtext("m:version", namespaces=ns)
require(artifact == "qca-sdk" and version, "Unexpected SDK coordinates")
require(group == "com.qoder.sdk" and artifact == "qoder-cloud-agents-sdk" and version,
"Unexpected SDK coordinates")
target = root / "target"
jars = [target / (artifact + "-" + version + suffix + ".jar")
for suffix in ("", "-sources", "-javadoc")]
Expand Down Expand Up @@ -52,7 +54,7 @@ def check(root):
"Internal implementation leaked into public Javadoc")
files = jars + [pom]
digests = {str(path.relative_to(root)): hashlib.sha256(path.read_bytes()).hexdigest() for path in files}
report = {"artifact_id": artifact, "version": version, "class_count": len(classes),
report = {"group_id": group, "artifact_id": artifact, "version": version, "class_count": len(classes),
"class_major_version": 52, "sha256": digests}
(target / "package-check.json").write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8")
(target / "SHA256SUMS").write_text("".join(digest + " " + name + "\n" for name, digest in digests.items()), encoding="utf-8")
Expand Down
6 changes: 3 additions & 3 deletions .github/scripts/check_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ def check_versions(root):
version = pom.findtext("m:version", namespaces=NS)
if not version or not re.fullmatch(r"[0-9]+\.[0-9]+\.[0-9]+(?:-[A-Za-z0-9.-]+)?", version):
raise ValueError("Use an explicit SDK version in pom.xml")
if pom.findtext("m:groupId", namespaces=NS) != "com.qoder" or pom.findtext("m:artifactId", namespaces=NS) != "qca-sdk":
if pom.findtext("m:groupId", namespaces=NS) != "com.qoder.sdk" or pom.findtext("m:artifactId", namespaces=NS) != "qoder-cloud-agents-sdk":
raise ValueError("Unexpected Maven coordinates")
consumer = ET.parse(root / "examples/pom.xml").getroot()
deps = [d for d in consumer.findall("m:dependencies/m:dependency", NS)
if d.findtext("m:groupId", namespaces=NS) == "com.qoder"
and d.findtext("m:artifactId", namespaces=NS) == "qca-sdk"]
if d.findtext("m:groupId", namespaces=NS) == "com.qoder.sdk"
and d.findtext("m:artifactId", namespaces=NS) == "qoder-cloud-agents-sdk"]
if len(deps) != 1 or deps[0].findtext("m:version", namespaces=NS) != version:
raise ValueError("examples SDK dependency must match the root POM")
if consumer.findtext("m:version", namespaces=NS) != version:
Expand Down
16 changes: 8 additions & 8 deletions .github/scripts/test_central_release.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@


def write_bundle(path, transform=lambda values: values):
prefix = 'com/qoder/qca-sdk/1.2.3/qca-sdk-1.2.3'
prefix = 'com/qoder/sdk/qoder-cloud-agents-sdk/1.2.3/qoder-cloud-agents-sdk-1.2.3'
values = {}
for suffix in ('.pom', '.jar', '-sources.jar', '-javadoc.jar'):
name = prefix + suffix
Expand Down Expand Up @@ -51,7 +51,7 @@ def setUp(self):

def status(self, state, **changes):
return json.dumps(dict(deploymentId=UUID, deploymentName=release.deployment_name(self.info),
deploymentState=state, purls=['pkg:maven/com.qoder/qca[email protected]'],
deploymentState=state, purls=['pkg:maven/com.qoder.sdk/qoder-cloud-agents[email protected]'],
**changes)).encode()

def test_identity_rejects_forks_events_and_snapshots(self):
Expand All @@ -62,7 +62,7 @@ def test_identity_rejects_forks_events_and_snapshots(self):

def test_bundle_requires_signatures_checksums_and_exact_coordinates(self):
self.assertEqual(self.info, release.load(self.output))
prefix = 'com/qoder/qca-sdk/1.2.3/qca-sdk-1.2.3'
prefix = 'com/qoder/sdk/qoder-cloud-agents-sdk/1.2.3/qoder-cloud-agents-sdk-1.2.3'
for name in (prefix + '.pom.asc', prefix + '.jar.md5'):
with self.subTest(name=name):
write_bundle(self.output / 'central-bundle.zip', lambda values: {k: v for k, v in values.items() if k != name})
Expand All @@ -73,7 +73,7 @@ def test_bundle_requires_signatures_checksums_and_exact_coordinates(self):
release.read_bundle(self.output / 'central-bundle.zip', '1.2.3')

def test_bundle_rejects_wrong_checksums_and_manifest_tampering(self):
key = 'com/qoder/qca-sdk/1.2.3/qca-sdk-1.2.3.jar.sha1'
key = 'com/qoder/sdk/qoder-cloud-agents-sdk/1.2.3/qoder-cloud-agents-sdk-1.2.3.jar.sha1'
write_bundle(self.output / 'central-bundle.zip', lambda values: dict(values, **{key: b'bad'}))
with self.assertRaisesRegex(ValueError, 'checksum mismatch'):
release.read_bundle(self.output / 'central-bundle.zip', '1.2.3')
Expand All @@ -88,13 +88,13 @@ def test_seal_detects_rebuild_differences(self):
release.save(candidate / 'intent.json', release.identity())
checks = {}
for suffix in ('.pom', '.jar', '-sources.jar', '-javadoc.jar'):
filename = 'qca-sdk-1.2.3' + suffix
filename = 'qoder-cloud-agents-sdk-1.2.3' + suffix
key = 'pom.xml' if suffix == '.pom' else 'target/' + filename
checks[key] = self.info['files']['com/qoder/qca-sdk/1.2.3/' + filename]
checks[key] = self.info['files']['com/qoder/sdk/qoder-cloud-agents-sdk/1.2.3/' + filename]
release.save(candidate / 'package-check.json', dict(sha256=checks))
(candidate / 'release-notes.md').write_text('Reviewed notes\n')
release.seal(source, candidate, self.output / 'sealed')
checks['target/qca-sdk-1.2.3.jar'] = '0' * 64
checks['target/qoder-cloud-agents-sdk-1.2.3.jar'] = '0' * 64
release.save(candidate / 'package-check.json', dict(sha256=checks))
with self.assertRaisesRegex(ValueError, 'differs from reviewed build'):
release.seal(source, candidate, self.output / 'sealed')
Expand Down Expand Up @@ -127,7 +127,7 @@ def test_tag_rejects_moved_sha_and_changed_bundle(self):

def test_preflight_without_environment_still_requires_current_main_and_ci(self):
source, candidate = self.output / 'source', self.output / 'candidate'
coordinates = '<groupId>com.qoder</groupId><artifactId>qca-sdk</artifactId><version>1.2.3</version>'
coordinates = '<groupId>com.qoder.sdk</groupId><artifactId>qoder-cloud-agents-sdk</artifactId><version>1.2.3</version>'
project = '<project xmlns="http://maven.apache.org/POM/4.0.0">{}</project>'
files = {
'pom.xml': project.format(coordinates),
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ jobs:
with:
name: candidate-java-${{ matrix.java }}
path: |
target/qca-sdk-*.jar
target/qoder-cloud-agents-sdk-*.jar
target/package-check.json
target/SHA256SUMS
pom.xml
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-preflight.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ jobs:
with:
name: release-candidate-${{ inputs.version }}
path: |
target/qca-sdk-*.jar
target/qoder-cloud-agents-sdk-*.jar
target/package-check.json
target/SHA256SUMS
pom.xml
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ jobs:
MAVEN_GPG_KEY: ${{ secrets.MAVEN_GPG_KEY }}
MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
run: |
test "$CENTRAL_NAMESPACE" = com.qoder || { echo 'Verify com.qoder in Central Portal and set the CENTRAL_NAMESPACE repository Actions variable'; exit 1; }
test "$CENTRAL_NAMESPACE" = com.qoder.sdk || { echo 'Verify publishing access to com.qoder.sdk in Central Portal and set the CENTRAL_NAMESPACE repository Actions variable'; exit 1; }
test -n "$CENTRAL_USERNAME" && test -n "$CENTRAL_PASSWORD" || { echo 'Central Portal token secrets are missing'; exit 1; }
if [[ -z "$RESUME_RUN_ID" ]]; then
test -n "$MAVEN_GPG_KEY" && test -n "$MAVEN_GPG_PASSPHRASE" || { echo 'GPG signing secrets are missing'; exit 1; }
Expand Down
10 changes: 9 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

## [Unreleased]

## [0.1.0-rc.1] - 2026-10-21

First public release candidate for installation and integration testing ahead of `0.1.0`.

### Changed

- Use `com.qoder.sdk:qoder-cloud-agents-sdk` as the Maven coordinates. Java packages remain under `com.qoder.cloudagents`.

### Fixed

- Repeated default header settings now replace earlier values case-insensitively, while per-request headers retain precedence.
Expand Down Expand Up @@ -68,4 +76,4 @@
- Usage, Token management and Drive round-trip examples with installed-JAR consumer checks, scoped cleanup and storage credential isolation.
- HTTP error request ID fallback from top-level and nested error bodies when response headers omit it.

The current SNAPSHOT can be built from source; Maven Central publication is not enabled. See the [maintenance guide](docs/maintaining.md) for current checks and delivery boundaries.
This version can be built from source; Maven Central publication remains pending. See the [maintenance guide](docs/maintaining.md) for current checks and delivery boundaries.
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,26 @@

Java 8 compatible client for the **Forward API**. The repository follows the resource and authentication structure of the QCA Go, Python and TypeScript SDKs, using Maven and Java builders.

**Status: source available; not yet published to Maven Central.** The current development version is `0.1.0-SNAPSHOT`. Build from source to use the SDK until a public Maven release is available.
**Status: source available; not yet published to Maven Central.** The current version is `0.1.0-rc.1`. Build from source to use the SDK until a public Maven release is available.

The SDK provides synchronous and asynchronous clients for Models, Templates, Identities, Sessions and Threads, Memory Stores, Environments, Files, Skills, Vaults and Credentials, Schedules, Batches, Channels and Pairing, Usage, Service Account Tokens and Drive. Shared support includes PAT/SA authentication, timeouts, retries, pagination, typed errors and request IDs. Session/Thread SSE supports explicit resumption; Realtime supports text, PCM audio and History. The [API coverage](docs/forward-api-coverage.md) records the 123-operation baseline and its exclusions. Managed APIs are outside this project's scope.

See the [examples](examples/README.md) for authentication and resource workflows, and the [maintenance guide](docs/maintaining.md) for contributor checks and version updates.

## Maven dependency

After installing the SDK locally or publishing it to your configured Maven repository, use:

```xml
<dependency>
<groupId>com.qoder.sdk</groupId>
<artifactId>qoder-cloud-agents-sdk</artifactId>
<version>0.1.0-rc.1</version>
</dependency>
```

The Maven coordinates do not change Java imports, which remain under `com.qoder.cloudagents`.

## Local build

```sh
Expand Down
6 changes: 3 additions & 3 deletions docs/maintaining.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,17 @@ For a release, move the applicable entries beneath `## [VERSION] - YYYY-MM-DD` a

- Root `pom.xml` project version.
- `examples/pom.xml` project version.
- The examples' `com.qoder:qca-sdk` dependency version.
- The examples' `com.qoder.sdk:qoder-cloud-agents-sdk` dependency version.

The SDK's embedded version is filtered from `${project.version}`; do not replace that template with a hard-coded value. `check_repository.py` rejects mismatched versions and requires a matching changelog entry for a non-SNAPSHOT version.

```sh
python3 .github/scripts/release_notes.py check
# After adding the actual release entry:
python3 .github/scripts/release_notes.py extract --version 0.1.0
python3 .github/scripts/release_notes.py extract --version 0.1.0-rc.1
```

The current version remains `0.1.0-SNAPSHOT`. A SNAPSHOT can resolve to different timestamped builds, so acceptance records must include the resolved version, source SHA and JAR digest. A successful test of an older SNAPSHOT is not evidence for a newer one.
The current version is `0.1.0-rc.1`. Acceptance records must include the resolved version, source SHA and JAR digest. Release versions must not be overwritten; a successful test of an older build is not evidence for a newer one.

## Build candidates and releases

Expand Down
Loading
Loading