diff --git a/components/ambient-sdk/generator/templates/ts/ambient_client.ts.tmpl b/components/ambient-sdk/generator/templates/ts/ambient_client.ts.tmpl index 03c038232..45acf7239 100644 --- a/components/ambient-sdk/generator/templates/ts/ambient_client.ts.tmpl +++ b/components/ambient-sdk/generator/templates/ts/ambient_client.ts.tmpl @@ -17,25 +17,29 @@ export class AmbientClient { if (!config.baseUrl) { throw new Error('baseUrl is required'); } - if (!config.token) { - throw new Error('token is required'); + if (config.token) { + if (config.token.length < 20) { + throw new Error('token is too short (minimum 20 characters)'); + } + if (config.token === 'YOUR_TOKEN_HERE' || config.token === 'PLACEHOLDER_TOKEN') { + throw new Error('placeholder token is not allowed'); + } } - if (config.token.length < 20) { - throw new Error('token is too short (minimum 20 characters)'); - } - if (config.token === 'YOUR_TOKEN_HERE' || config.token === 'PLACEHOLDER_TOKEN') { - throw new Error('placeholder token is not allowed'); - } - if (!config.project) { - throw new Error('project is required'); - } - if (config.project.length > 63) { + if (config.project && config.project.length > 63) { throw new Error('project name cannot exceed 63 characters'); } - const url = new URL(config.baseUrl); - if (url.protocol !== 'http:' && url.protocol !== 'https:') { - throw new Error('only HTTP and HTTPS schemes are supported'); + // Reject protocol-relative URLs (e.g. "//evil.com/path") + if (config.baseUrl.startsWith('//')) { + throw new Error('Protocol-relative URLs are not allowed for baseUrl'); + } + + // Skip URL parsing for relative paths (e.g. "/api/proxy") + if (!config.baseUrl.startsWith('/')) { + const url = new URL(config.baseUrl); + if (url.protocol !== 'http:' && url.protocol !== 'https:') { + throw new Error('only HTTP and HTTPS schemes are supported'); + } } this.config = { @@ -54,10 +58,7 @@ export class AmbientClient { if (!token) { throw new Error('AMBIENT_TOKEN environment variable is required'); } - if (!project) { - throw new Error('AMBIENT_PROJECT environment variable is required'); - } - return new AmbientClient({ baseUrl, token, project }); + return new AmbientClient({ baseUrl, token, ...(project ? { project } : {}) }); } } diff --git a/components/ambient-sdk/generator/templates/ts/base.ts.tmpl b/components/ambient-sdk/generator/templates/ts/base.ts.tmpl index ba0ae525d..376edc44e 100644 --- a/components/ambient-sdk/generator/templates/ts/base.ts.tmpl +++ b/components/ambient-sdk/generator/templates/ts/base.ts.tmpl @@ -70,8 +70,8 @@ export type RequestOptions = { export type AmbientClientConfig = { baseUrl: string; - token: string; - project: string; + token?: string; + project?: string; }; export async function ambientFetch( @@ -83,8 +83,8 @@ export async function ambientFetch( ): Promise { const url = `${config.baseUrl}{{.Spec.BasePath}}${path}`; const headers: Record = { - 'Authorization': `Bearer ${config.token}`, - 'X-Ambient-Project': config.project, + ...(config.token ? { 'Authorization': `Bearer ${config.token}` } : {}), + ...(config.project ? { 'X-Ambient-Project': config.project } : {}), }; if (body !== undefined) { headers['Content-Type'] = 'application/json'; diff --git a/components/ambient-sdk/generator/templates/ts/client.ts.tmpl b/components/ambient-sdk/generator/templates/ts/client.ts.tmpl index e99d175b2..36fb73839 100644 --- a/components/ambient-sdk/generator/templates/ts/client.ts.tmpl +++ b/components/ambient-sdk/generator/templates/ts/client.ts.tmpl @@ -12,6 +12,9 @@ export class {{.Resource.Name}}API { {{- if .Resource.IsSubResource}} private basePath(): string { + if (!this.config.project) { + throw new Error('project is required for {{.Resource.Name}} operations'); + } return '/{{.Resource.PathSegment}}'.replace('{id}', encodeURIComponent(this.config.project)); } {{end}} diff --git a/components/ambient-sdk/go-sdk/types/scheduled_session.go b/components/ambient-sdk/go-sdk/types/scheduled_session.go old mode 100755 new mode 100644 index 2b3f9d2ce..365e62851 --- a/components/ambient-sdk/go-sdk/types/scheduled_session.go +++ b/components/ambient-sdk/go-sdk/types/scheduled_session.go @@ -38,17 +38,17 @@ func (l *ScheduledSessionList) GetSize() int { return l.Size } // ScheduledSessionPatch is the request body for a PATCH operation. // Only set fields that should be changed; omitted (nil) fields are left unchanged. type ScheduledSessionPatch struct { + AgentID *string `json:"agent_id,omitempty"` Name *string `json:"name,omitempty"` Description *string `json:"description,omitempty"` - AgentID *string `json:"agent_id,omitempty"` Schedule *string `json:"schedule,omitempty"` Timezone *string `json:"timezone,omitempty"` Enabled *bool `json:"enabled,omitempty"` - SessionPrompt *string `json:"session_prompt,omitempty"` - Timeout *int32 `json:"timeout,omitempty"` InactivityTimeout *int32 `json:"inactivity_timeout,omitempty"` - StopOnRunFinished *bool `json:"stop_on_run_finished,omitempty"` RunnerType *string `json:"runner_type,omitempty"` + SessionPrompt *string `json:"session_prompt,omitempty"` + StopOnRunFinished *bool `json:"stop_on_run_finished,omitempty"` + Timeout *int32 `json:"timeout,omitempty"` } // ScheduledSessionBuilder builds a ScheduledSession for creation. @@ -120,6 +120,9 @@ func (b *ScheduledSessionBuilder) Build() (*ScheduledSession, error) { if b.resource.Name == "" { b.errs = append(b.errs, fmt.Errorf("name is required")) } + if b.resource.AgentID == "" { + b.errs = append(b.errs, fmt.Errorf("agent_id is required")) + } if b.resource.Schedule == "" { b.errs = append(b.errs, fmt.Errorf("schedule is required")) } diff --git a/components/ambient-sdk/python-sdk/ambient_platform/_credential_api.py b/components/ambient-sdk/python-sdk/ambient_platform/_credential_api.py new file mode 100644 index 000000000..b22bae1fd --- /dev/null +++ b/components/ambient-sdk/python-sdk/ambient_platform/_credential_api.py @@ -0,0 +1,52 @@ +# Code generated by ambient-sdk-generator from openapi.yaml — DO NOT EDIT. +# Source: ../../ambient-api-server/openapi/openapi.yaml +# Spec SHA256: c9d4494778eb0a006db1f289630460fb9b5dc58df1895903c42735b4f56b0314 +# Generated: 2026-05-05T15:33:32Z + +from __future__ import annotations + +from typing import Any, Iterator, Optional, TYPE_CHECKING +from urllib.parse import quote + +from ._base import ListOptions +from .credential import Credential, CredentialList + +if TYPE_CHECKING: + from .client import AmbientClient + + +class CredentialAPI: + def __init__(self, client: AmbientClient) -> None: + self._client = client + def _base_path(self) -> str: + return "/projects/{id}/credentials".replace("{id}", quote(self._client._project, safe="")) + + + def create(self, data: dict) -> Credential: + resp = self._client._request("POST", self._base_path(), json=data) + return Credential.from_dict(resp) + + def get(self, resource_id: str) -> Credential: + resp = self._client._request("GET", f"{self._base_path()}/{resource_id}") + return Credential.from_dict(resp) + + def list(self, opts: Optional[ListOptions] = None) -> CredentialList: + params = opts.to_params() if opts else None + resp = self._client._request("GET", self._base_path(), params=params) + return CredentialList.from_dict(resp) + def update(self, resource_id: str, patch: Any) -> Credential: + data = patch.to_dict() if hasattr(patch, "to_dict") else patch + resp = self._client._request("PATCH", f"{self._base_path()}/{resource_id}", json=data) + return Credential.from_dict(resp) + + def delete(self, resource_id: str) -> None: + self._client._request("DELETE", f"{self._base_path()}/{resource_id}", expect_json=False) + + def list_all(self, size: int = 100, **kwargs: Any) -> Iterator[Credential]: + page = 1 + while True: + result = self.list(ListOptions().page(page).size(size)) + yield from result.items + if page * size >= result.total: + break + page += 1 diff --git a/components/ambient-sdk/python-sdk/ambient_platform/_scheduled_session_api.py b/components/ambient-sdk/python-sdk/ambient_platform/_scheduled_session_api.py new file mode 100644 index 000000000..ce52c9a55 --- /dev/null +++ b/components/ambient-sdk/python-sdk/ambient_platform/_scheduled_session_api.py @@ -0,0 +1,52 @@ +# Code generated by ambient-sdk-generator from openapi.yaml — DO NOT EDIT. +# Source: ../../ambient-api-server/openapi/openapi.yaml +# Spec SHA256: c9d4494778eb0a006db1f289630460fb9b5dc58df1895903c42735b4f56b0314 +# Generated: 2026-05-05T15:33:32Z + +from __future__ import annotations + +from typing import Any, Iterator, Optional, TYPE_CHECKING +from urllib.parse import quote + +from ._base import ListOptions +from .scheduled_session import ScheduledSession, ScheduledSessionList + +if TYPE_CHECKING: + from .client import AmbientClient + + +class ScheduledSessionAPI: + def __init__(self, client: AmbientClient) -> None: + self._client = client + def _base_path(self) -> str: + return "/projects/{id}/scheduled-sessions".replace("{id}", quote(self._client._project, safe="")) + + + def create(self, data: dict) -> ScheduledSession: + resp = self._client._request("POST", self._base_path(), json=data) + return ScheduledSession.from_dict(resp) + + def get(self, resource_id: str) -> ScheduledSession: + resp = self._client._request("GET", f"{self._base_path()}/{resource_id}") + return ScheduledSession.from_dict(resp) + + def list(self, opts: Optional[ListOptions] = None) -> ScheduledSessionList: + params = opts.to_params() if opts else None + resp = self._client._request("GET", self._base_path(), params=params) + return ScheduledSessionList.from_dict(resp) + def update(self, resource_id: str, patch: Any) -> ScheduledSession: + data = patch.to_dict() if hasattr(patch, "to_dict") else patch + resp = self._client._request("PATCH", f"{self._base_path()}/{resource_id}", json=data) + return ScheduledSession.from_dict(resp) + + def delete(self, resource_id: str) -> None: + self._client._request("DELETE", f"{self._base_path()}/{resource_id}", expect_json=False) + + def list_all(self, size: int = 100, **kwargs: Any) -> Iterator[ScheduledSession]: + page = 1 + while True: + result = self.list(ListOptions().page(page).size(size)) + yield from result.items + if page * size >= result.total: + break + page += 1 diff --git a/components/ambient-sdk/python-sdk/ambient_platform/credential.py b/components/ambient-sdk/python-sdk/ambient_platform/credential.py new file mode 100644 index 000000000..3b7f75a1c --- /dev/null +++ b/components/ambient-sdk/python-sdk/ambient_platform/credential.py @@ -0,0 +1,164 @@ +# Code generated by ambient-sdk-generator from openapi.yaml — DO NOT EDIT. +# Source: ../../ambient-api-server/openapi/openapi.yaml +# Spec SHA256: c9d4494778eb0a006db1f289630460fb9b5dc58df1895903c42735b4f56b0314 +# Generated: 2026-05-05T15:33:32Z + +from __future__ import annotations + +from dataclasses import dataclass +from datetime import datetime +from typing import Any, Optional + +from ._base import ListMeta, _parse_datetime + + +@dataclass(frozen=True) +class Credential: + id: str = "" + kind: str = "" + href: str = "" + created_at: Optional[datetime] = None + updated_at: Optional[datetime] = None + annotations: str = "" + description: str = "" + email: str = "" + labels: str = "" + name: str = "" + project_id: str = "" + provider: str = "" + token: str = "" + url: str = "" + + @classmethod + def from_dict(cls, data: dict) -> Credential: + return cls( + id=data.get("id", ""), + kind=data.get("kind", ""), + href=data.get("href", ""), + created_at=_parse_datetime(data.get("created_at")), + updated_at=_parse_datetime(data.get("updated_at")), + annotations=data.get("annotations", ""), + description=data.get("description", ""), + email=data.get("email", ""), + labels=data.get("labels", ""), + name=data.get("name", ""), + project_id=data.get("project_id", ""), + provider=data.get("provider", ""), + token=data.get("token", ""), + url=data.get("url", ""), + ) + + @classmethod + def builder(cls) -> CredentialBuilder: + return CredentialBuilder() + + +@dataclass(frozen=True) +class CredentialList: + kind: str = "" + page: int = 0 + size: int = 0 + total: int = 0 + items: list[Credential] = () + + @classmethod + def from_dict(cls, data: dict) -> CredentialList: + return cls( + kind=data.get("kind", ""), + page=data.get("page", 0), + size=data.get("size", 0), + total=data.get("total", 0), + items=[Credential.from_dict(item) for item in data.get("items", [])], + ) + + +class CredentialBuilder: + def __init__(self) -> None: + self._data: dict[str, Any] = {} + + + def annotations(self, value: str) -> CredentialBuilder: + self._data["annotations"] = value + return self + + def description(self, value: str) -> CredentialBuilder: + self._data["description"] = value + return self + + def email(self, value: str) -> CredentialBuilder: + self._data["email"] = value + return self + + def labels(self, value: str) -> CredentialBuilder: + self._data["labels"] = value + return self + + def name(self, value: str) -> CredentialBuilder: + self._data["name"] = value + return self + + def project_id(self, value: str) -> CredentialBuilder: + self._data["project_id"] = value + return self + + def provider(self, value: str) -> CredentialBuilder: + self._data["provider"] = value + return self + + def token(self, value: str) -> CredentialBuilder: + self._data["token"] = value + return self + + def url(self, value: str) -> CredentialBuilder: + self._data["url"] = value + return self + + def build(self) -> dict: + if "name" not in self._data: + raise ValueError("name is required") + if "project_id" not in self._data: + raise ValueError("project_id is required") + if "provider" not in self._data: + raise ValueError("provider is required") + return dict(self._data) + + +class CredentialPatch: + def __init__(self) -> None: + self._data: dict[str, Any] = {} + + + def annotations(self, value: str) -> CredentialPatch: + self._data["annotations"] = value + return self + + def description(self, value: str) -> CredentialPatch: + self._data["description"] = value + return self + + def email(self, value: str) -> CredentialPatch: + self._data["email"] = value + return self + + def labels(self, value: str) -> CredentialPatch: + self._data["labels"] = value + return self + + def name(self, value: str) -> CredentialPatch: + self._data["name"] = value + return self + + def provider(self, value: str) -> CredentialPatch: + self._data["provider"] = value + return self + + def token(self, value: str) -> CredentialPatch: + self._data["token"] = value + return self + + def url(self, value: str) -> CredentialPatch: + self._data["url"] = value + return self + + def to_dict(self) -> dict: + return dict(self._data) diff --git a/components/ambient-sdk/python-sdk/ambient_platform/scheduled_session.py b/components/ambient-sdk/python-sdk/ambient_platform/scheduled_session.py new file mode 100644 index 000000000..28a2b976a --- /dev/null +++ b/components/ambient-sdk/python-sdk/ambient_platform/scheduled_session.py @@ -0,0 +1,206 @@ +# Code generated by ambient-sdk-generator from openapi.yaml — DO NOT EDIT. +# Source: ../../ambient-api-server/openapi/openapi.yaml +# Spec SHA256: c9d4494778eb0a006db1f289630460fb9b5dc58df1895903c42735b4f56b0314 +# Generated: 2026-05-05T15:33:32Z + +from __future__ import annotations + +from dataclasses import dataclass +from datetime import datetime +from typing import Any, Optional + +from ._base import ListMeta, _parse_datetime + + +@dataclass(frozen=True) +class ScheduledSession: + id: str = "" + kind: str = "" + href: str = "" + created_at: Optional[datetime] = None + updated_at: Optional[datetime] = None + agent_id: str = "" + description: str = "" + enabled: bool = False + inactivity_timeout: int = 0 + last_run_at: Optional[datetime] = None + name: str = "" + next_run_at: Optional[datetime] = None + project_id: str = "" + runner_type: str = "" + schedule: str = "" + session_prompt: str = "" + stop_on_run_finished: bool = False + timeout: int = 0 + timezone: str = "" + + @classmethod + def from_dict(cls, data: dict) -> ScheduledSession: + return cls( + id=data.get("id", ""), + kind=data.get("kind", ""), + href=data.get("href", ""), + created_at=_parse_datetime(data.get("created_at")), + updated_at=_parse_datetime(data.get("updated_at")), + agent_id=data.get("agent_id", ""), + description=data.get("description", ""), + enabled=data.get("enabled", False), + inactivity_timeout=data.get("inactivity_timeout", 0), + last_run_at=_parse_datetime(data.get("last_run_at")), + name=data.get("name", ""), + next_run_at=_parse_datetime(data.get("next_run_at")), + project_id=data.get("project_id", ""), + runner_type=data.get("runner_type", ""), + schedule=data.get("schedule", ""), + session_prompt=data.get("session_prompt", ""), + stop_on_run_finished=data.get("stop_on_run_finished", False), + timeout=data.get("timeout", 0), + timezone=data.get("timezone", ""), + ) + + @classmethod + def builder(cls) -> ScheduledSessionBuilder: + return ScheduledSessionBuilder() + + +@dataclass(frozen=True) +class ScheduledSessionList: + kind: str = "" + page: int = 0 + size: int = 0 + total: int = 0 + items: list[ScheduledSession] = () + + @classmethod + def from_dict(cls, data: dict) -> ScheduledSessionList: + return cls( + kind=data.get("kind", ""), + page=data.get("page", 0), + size=data.get("size", 0), + total=data.get("total", 0), + items=[ScheduledSession.from_dict(item) for item in data.get("items", [])], + ) + + +class ScheduledSessionBuilder: + def __init__(self) -> None: + self._data: dict[str, Any] = {} + + + def agent_id(self, value: str) -> ScheduledSessionBuilder: + self._data["agent_id"] = value + return self + + def description(self, value: str) -> ScheduledSessionBuilder: + self._data["description"] = value + return self + + def enabled(self, value: bool) -> ScheduledSessionBuilder: + self._data["enabled"] = value + return self + + def inactivity_timeout(self, value: int) -> ScheduledSessionBuilder: + self._data["inactivity_timeout"] = value + return self + + def last_run_at(self, value: Optional[datetime]) -> ScheduledSessionBuilder: + self._data["last_run_at"] = value + return self + + def name(self, value: str) -> ScheduledSessionBuilder: + self._data["name"] = value + return self + + def next_run_at(self, value: Optional[datetime]) -> ScheduledSessionBuilder: + self._data["next_run_at"] = value + return self + + def project_id(self, value: str) -> ScheduledSessionBuilder: + self._data["project_id"] = value + return self + + def runner_type(self, value: str) -> ScheduledSessionBuilder: + self._data["runner_type"] = value + return self + + def schedule(self, value: str) -> ScheduledSessionBuilder: + self._data["schedule"] = value + return self + + def session_prompt(self, value: str) -> ScheduledSessionBuilder: + self._data["session_prompt"] = value + return self + + def stop_on_run_finished(self, value: bool) -> ScheduledSessionBuilder: + self._data["stop_on_run_finished"] = value + return self + + def timeout(self, value: int) -> ScheduledSessionBuilder: + self._data["timeout"] = value + return self + + def timezone(self, value: str) -> ScheduledSessionBuilder: + self._data["timezone"] = value + return self + + def build(self) -> dict: + if "name" not in self._data: + raise ValueError("name is required") + if "project_id" not in self._data: + raise ValueError("project_id is required") + if "schedule" not in self._data: + raise ValueError("schedule is required") + return dict(self._data) + + +class ScheduledSessionPatch: + def __init__(self) -> None: + self._data: dict[str, Any] = {} + + + def agent_id(self, value: str) -> ScheduledSessionPatch: + self._data["agent_id"] = value + return self + + def description(self, value: str) -> ScheduledSessionPatch: + self._data["description"] = value + return self + + def enabled(self, value: bool) -> ScheduledSessionPatch: + self._data["enabled"] = value + return self + + def inactivity_timeout(self, value: int) -> ScheduledSessionPatch: + self._data["inactivity_timeout"] = value + return self + + def name(self, value: str) -> ScheduledSessionPatch: + self._data["name"] = value + return self + + def runner_type(self, value: str) -> ScheduledSessionPatch: + self._data["runner_type"] = value + return self + + def schedule(self, value: str) -> ScheduledSessionPatch: + self._data["schedule"] = value + return self + + def session_prompt(self, value: str) -> ScheduledSessionPatch: + self._data["session_prompt"] = value + return self + + def stop_on_run_finished(self, value: bool) -> ScheduledSessionPatch: + self._data["stop_on_run_finished"] = value + return self + + def timeout(self, value: int) -> ScheduledSessionPatch: + self._data["timeout"] = value + return self + + def timezone(self, value: str) -> ScheduledSessionPatch: + self._data["timezone"] = value + return self + + def to_dict(self) -> dict: + return dict(self._data) diff --git a/components/ambient-sdk/ts-sdk/src/agent.ts b/components/ambient-sdk/ts-sdk/src/agent.ts index a4788819d..37009f398 100644 --- a/components/ambient-sdk/ts-sdk/src/agent.ts +++ b/components/ambient-sdk/ts-sdk/src/agent.ts @@ -1,19 +1,29 @@ // Code generated by ambient-sdk-generator from openapi.yaml — DO NOT EDIT. // Source: ../../ambient-api-server/openapi/openapi.yaml -// Spec SHA256: 404b5671af96841f4a876b6192afc6358b4fb8e082ca2b6da499efcf3f72c781 -// Generated: 2026-03-20T16:48:23Z +// Spec SHA256: c9d4494778eb0a006db1f289630460fb9b5dc58df1895903c42735b4f56b0314 +// Generated: 2026-05-05T15:50:22Z import type { ObjectReference, ListMeta } from './base'; export type Agent = ObjectReference & { - annotations?: string; - current_session_id?: string; - labels?: string; + annotations: string; + bot_account_name: string; + current_session_id: string; + description: string; + display_name: string; + environment_variables: string; + labels: string; + llm_max_tokens: number; + llm_model: string; + llm_temperature: number; name: string; - owner_user_id?: string; - project_id?: string; - prompt?: string; - version?: number; + owner_user_id: string; + parent_agent_id: string; + project_id: string; + prompt: string; + repo_url: string; + resource_overrides: string; + workflow_id: string; }; export type AgentList = ListMeta & { @@ -22,33 +32,86 @@ export type AgentList = ListMeta & { export type AgentCreateRequest = { annotations?: string; + bot_account_name?: string; + description?: string; + display_name?: string; + environment_variables?: string; labels?: string; + llm_max_tokens?: number; + llm_model?: string; + llm_temperature?: number; name: string; owner_user_id?: string; - project_id?: string; + parent_agent_id?: string; + project_id: string; prompt?: string; + repo_url?: string; + resource_overrides?: string; + workflow_id?: string; }; export type AgentPatchRequest = { annotations?: string; + description?: string; + display_name?: string; labels?: string; + llm_max_tokens?: number; + llm_model?: string; + llm_temperature?: number; name?: string; prompt?: string; + repo_url?: string; }; export class AgentBuilder { private data: Record = {}; + annotations(value: string): this { this.data['annotations'] = value; return this; } + botAccountName(value: string): this { + this.data['bot_account_name'] = value; + return this; + } + + description(value: string): this { + this.data['description'] = value; + return this; + } + + displayName(value: string): this { + this.data['display_name'] = value; + return this; + } + + environmentVariables(value: string): this { + this.data['environment_variables'] = value; + return this; + } + labels(value: string): this { this.data['labels'] = value; return this; } + llmMaxTokens(value: number): this { + this.data['llm_max_tokens'] = value; + return this; + } + + llmModel(value: string): this { + this.data['llm_model'] = value; + return this; + } + + llmTemperature(value: number): this { + this.data['llm_temperature'] = value; + return this; + } + name(value: string): this { this.data['name'] = value; return this; @@ -59,6 +122,11 @@ export class AgentBuilder { return this; } + parentAgentId(value: string): this { + this.data['parent_agent_id'] = value; + return this; + } + projectId(value: string): this { this.data['project_id'] = value; return this; @@ -69,10 +137,28 @@ export class AgentBuilder { return this; } + repoUrl(value: string): this { + this.data['repo_url'] = value; + return this; + } + + resourceOverrides(value: string): this { + this.data['resource_overrides'] = value; + return this; + } + + workflowId(value: string): this { + this.data['workflow_id'] = value; + return this; + } + build(): AgentCreateRequest { if (!this.data['name']) { throw new Error('name is required'); } + if (!this.data['project_id']) { + throw new Error('project_id is required'); + } return this.data as AgentCreateRequest; } } @@ -80,16 +166,42 @@ export class AgentBuilder { export class AgentPatchBuilder { private data: Record = {}; + annotations(value: string): this { this.data['annotations'] = value; return this; } + description(value: string): this { + this.data['description'] = value; + return this; + } + + displayName(value: string): this { + this.data['display_name'] = value; + return this; + } + labels(value: string): this { this.data['labels'] = value; return this; } + llmMaxTokens(value: number): this { + this.data['llm_max_tokens'] = value; + return this; + } + + llmModel(value: string): this { + this.data['llm_model'] = value; + return this; + } + + llmTemperature(value: number): this { + this.data['llm_temperature'] = value; + return this; + } + name(value: string): this { this.data['name'] = value; return this; @@ -100,6 +212,11 @@ export class AgentPatchBuilder { return this; } + repoUrl(value: string): this { + this.data['repo_url'] = value; + return this; + } + build(): AgentPatchRequest { return this.data as AgentPatchRequest; } diff --git a/components/ambient-sdk/ts-sdk/src/agent_api.ts b/components/ambient-sdk/ts-sdk/src/agent_api.ts index e90a05cec..5a567ff12 100644 --- a/components/ambient-sdk/ts-sdk/src/agent_api.ts +++ b/components/ambient-sdk/ts-sdk/src/agent_api.ts @@ -1,42 +1,44 @@ // Code generated by ambient-sdk-generator from openapi.yaml — DO NOT EDIT. // Source: ../../ambient-api-server/openapi/openapi.yaml -// Spec SHA256: 404b5671af96841f4a876b6192afc6358b4fb8e082ca2b6da499efcf3f72c781 -// Generated: 2026-03-20T16:48:23Z +// Spec SHA256: c9d4494778eb0a006db1f289630460fb9b5dc58df1895903c42735b4f56b0314 +// Generated: 2026-05-05T15:50:22Z -import type { AmbientClientConfig, ListOptions, RequestOptions, ListMeta } from './base'; +import type { AmbientClientConfig, ListOptions, RequestOptions } from './base'; import { ambientFetch, buildQueryString } from './base'; import type { Agent, AgentList, AgentCreateRequest, AgentPatchRequest } from './agent'; -import type { Session } from './session'; - -export type AgentSessionList = ListMeta & { items: Session[] }; - -export type StartRequest = { - prompt?: string; -}; - -export type StartResponse = { - session?: Session; - starting_prompt?: string; -}; export class AgentAPI { constructor(private readonly config: AmbientClientConfig) {} + private basePath(): string { + if (!this.config.project) { + throw new Error('project is required for Agent operations'); + } + return '/projects/{id}/agents'.replace('{id}', encodeURIComponent(this.config.project)); + } + async create(data: AgentCreateRequest, opts?: RequestOptions): Promise { - return ambientFetch(this.config, 'POST', '/agents', data, opts); + return ambientFetch(this.config, 'POST', this.basePath(), data, opts); } async get(id: string, opts?: RequestOptions): Promise { - return ambientFetch(this.config, 'GET', `/agents/${id}`, undefined, opts); + return ambientFetch(this.config, 'GET', `${this.basePath()}/${id}`, undefined, opts); } async list(listOpts?: ListOptions, opts?: RequestOptions): Promise { const qs = buildQueryString(listOpts); - return ambientFetch(this.config, 'GET', `/agents${qs}`, undefined, opts); + return ambientFetch(this.config, 'GET', `${this.basePath()}${qs}`, undefined, opts); } - async update(id: string, patch: AgentPatchRequest, opts?: RequestOptions): Promise { - return ambientFetch(this.config, 'PATCH', `/agents/${id}`, patch, opts); + return ambientFetch(this.config, 'PATCH', `${this.basePath()}/${id}`, patch, opts); + } + + async delete(id: string, opts?: RequestOptions): Promise { + return ambientFetch(this.config, 'DELETE', `${this.basePath()}/${id}`, undefined, opts); + } + + async start(id: string, opts?: RequestOptions): Promise { + return ambientFetch(this.config, 'POST', `${this.basePath()}/${id}/start`, undefined, opts); } async *listAll(size: number = 100, opts?: RequestOptions): AsyncGenerator { @@ -52,38 +54,4 @@ export class AgentAPI { page++; } } - - async listByProject(projectId: string, listOpts?: ListOptions, opts?: RequestOptions): Promise { - const qs = buildQueryString(listOpts); - return ambientFetch(this.config, 'GET', `/projects/${encodeURIComponent(projectId)}/agents${qs}`, undefined, opts); - } - - async getByProject(projectId: string, agentId: string, opts?: RequestOptions): Promise { - return ambientFetch(this.config, 'GET', `/projects/${encodeURIComponent(projectId)}/agents/${encodeURIComponent(agentId)}`, undefined, opts); - } - - async createInProject(projectId: string, data: AgentCreateRequest, opts?: RequestOptions): Promise { - return ambientFetch(this.config, 'POST', `/projects/${encodeURIComponent(projectId)}/agents`, data, opts); - } - - async updateInProject(projectId: string, agentId: string, patch: AgentPatchRequest, opts?: RequestOptions): Promise { - return ambientFetch(this.config, 'PATCH', `/projects/${encodeURIComponent(projectId)}/agents/${encodeURIComponent(agentId)}`, patch, opts); - } - - async deleteInProject(projectId: string, agentId: string, opts?: RequestOptions): Promise { - return ambientFetch(this.config, 'DELETE', `/projects/${encodeURIComponent(projectId)}/agents/${encodeURIComponent(agentId)}`, undefined, opts); - } - - async start(projectId: string, agentId: string, prompt?: string, opts?: RequestOptions): Promise { - return ambientFetch(this.config, 'POST', `/projects/${encodeURIComponent(projectId)}/agents/${encodeURIComponent(agentId)}/start`, { prompt }, opts); - } - - async getStartPreview(projectId: string, agentId: string, opts?: RequestOptions): Promise { - return ambientFetch(this.config, 'GET', `/projects/${encodeURIComponent(projectId)}/agents/${encodeURIComponent(agentId)}/start`, undefined, opts); - } - - async sessions(projectId: string, agentId: string, listOpts?: ListOptions, opts?: RequestOptions): Promise { - const qs = buildQueryString(listOpts); - return ambientFetch(this.config, 'GET', `/projects/${encodeURIComponent(projectId)}/agents/${encodeURIComponent(agentId)}/sessions${qs}`, undefined, opts); - } } diff --git a/components/ambient-sdk/ts-sdk/src/base.ts b/components/ambient-sdk/ts-sdk/src/base.ts index 5994cb43b..98656c9b5 100644 --- a/components/ambient-sdk/ts-sdk/src/base.ts +++ b/components/ambient-sdk/ts-sdk/src/base.ts @@ -1,7 +1,7 @@ // Code generated by ambient-sdk-generator from openapi.yaml — DO NOT EDIT. // Source: ../../ambient-api-server/openapi/openapi.yaml -// Spec SHA256: 9a8e623edcfae33acf56edf974d1859a127c22915d4831cb786daba2b398ca37 -// Generated: 2026-03-21T21:30:53Z +// Spec SHA256: c9d4494778eb0a006db1f289630460fb9b5dc58df1895903c42735b4f56b0314 +// Generated: 2026-05-05T15:50:22Z export type ObjectReference = { id: string; @@ -70,7 +70,7 @@ export type RequestOptions = { export type AmbientClientConfig = { baseUrl: string; - token: string; + token?: string; project?: string; }; @@ -83,7 +83,7 @@ export async function ambientFetch( ): Promise { const url = `${config.baseUrl}/api/ambient/v1${path}`; const headers: Record = { - 'Authorization': `Bearer ${config.token}`, + ...(config.token ? { 'Authorization': `Bearer ${config.token}` } : {}), ...(config.project ? { 'X-Ambient-Project': config.project } : {}), }; if (body !== undefined) { diff --git a/components/ambient-sdk/ts-sdk/src/client.ts b/components/ambient-sdk/ts-sdk/src/client.ts index 941a077ea..74ddd9c04 100644 --- a/components/ambient-sdk/ts-sdk/src/client.ts +++ b/components/ambient-sdk/ts-sdk/src/client.ts @@ -1,15 +1,17 @@ // Code generated by ambient-sdk-generator from openapi.yaml — DO NOT EDIT. // Source: ../../ambient-api-server/openapi/openapi.yaml -// Spec SHA256: 9a8e623edcfae33acf56edf974d1859a127c22915d4831cb786daba2b398ca37 -// Generated: 2026-03-21T21:30:53Z +// Spec SHA256: c9d4494778eb0a006db1f289630460fb9b5dc58df1895903c42735b4f56b0314 +// Generated: 2026-05-05T15:50:22Z import type { AmbientClientConfig } from './base'; +import { AgentAPI } from './agent_api'; +import { CredentialAPI } from './credential_api'; import { InboxMessageAPI } from './inbox_message_api'; import { ProjectAPI } from './project_api'; -import { AgentAPI } from './agent_api'; import { ProjectSettingsAPI } from './project_settings_api'; import { RoleAPI } from './role_api'; import { RoleBindingAPI } from './role_binding_api'; +import { ScheduledSessionAPI } from './scheduled_session_api'; import { SessionAPI } from './session_api'; import { SessionMessageAPI } from './session_message_api'; import { UserAPI } from './user_api'; @@ -18,12 +20,14 @@ import { UserAPI } from './user_api'; export class AmbientClient { private readonly config: AmbientClientConfig; + readonly agents: AgentAPI; + readonly credentials: CredentialAPI; readonly inboxMessages: InboxMessageAPI; readonly projects: ProjectAPI; - readonly agents: AgentAPI; readonly projectSettings: ProjectSettingsAPI; readonly roles: RoleAPI; readonly roleBindings: RoleBindingAPI; + readonly scheduledSessions: ScheduledSessionAPI; readonly sessions: SessionAPI; readonly sessionMessages: SessionMessageAPI; readonly users: UserAPI; @@ -32,22 +36,29 @@ export class AmbientClient { if (!config.baseUrl) { throw new Error('baseUrl is required'); } - if (!config.token) { - throw new Error('token is required'); - } - if (config.token.length < 20) { - throw new Error('token is too short (minimum 20 characters)'); - } - if (config.token === 'YOUR_TOKEN_HERE' || config.token === 'PLACEHOLDER_TOKEN') { - throw new Error('placeholder token is not allowed'); + if (config.token) { + if (config.token.length < 20) { + throw new Error('token is too short (minimum 20 characters)'); + } + if (config.token === 'YOUR_TOKEN_HERE' || config.token === 'PLACEHOLDER_TOKEN') { + throw new Error('placeholder token is not allowed'); + } } if (config.project && config.project.length > 63) { throw new Error('project name cannot exceed 63 characters'); } - const url = new URL(config.baseUrl); - if (url.protocol !== 'http:' && url.protocol !== 'https:') { - throw new Error('only HTTP and HTTPS schemes are supported'); + // Reject protocol-relative URLs (e.g. "//evil.com/path") + if (config.baseUrl.startsWith('//')) { + throw new Error('Protocol-relative URLs are not allowed for baseUrl'); + } + + // Skip URL parsing for relative paths (e.g. "/api/proxy") + if (!config.baseUrl.startsWith('/')) { + const url = new URL(config.baseUrl); + if (url.protocol !== 'http:' && url.protocol !== 'https:') { + throw new Error('only HTTP and HTTPS schemes are supported'); + } } this.config = { @@ -55,12 +66,14 @@ export class AmbientClient { baseUrl: config.baseUrl.replace(/\/+$/, ''), }; + this.agents = new AgentAPI(this.config); + this.credentials = new CredentialAPI(this.config); this.inboxMessages = new InboxMessageAPI(this.config); this.projects = new ProjectAPI(this.config); - this.agents = new AgentAPI(this.config); this.projectSettings = new ProjectSettingsAPI(this.config); this.roles = new RoleAPI(this.config); this.roleBindings = new RoleBindingAPI(this.config); + this.scheduledSessions = new ScheduledSessionAPI(this.config); this.sessions = new SessionAPI(this.config); this.sessionMessages = new SessionMessageAPI(this.config); this.users = new UserAPI(this.config); @@ -74,6 +87,7 @@ export class AmbientClient { if (!token) { throw new Error('AMBIENT_TOKEN environment variable is required'); } + return new AmbientClient({ baseUrl, token, ...(project ? { project } : {}) }); } } diff --git a/components/ambient-sdk/ts-sdk/src/credential.ts b/components/ambient-sdk/ts-sdk/src/credential.ts new file mode 100644 index 000000000..dbd1a5ff5 --- /dev/null +++ b/components/ambient-sdk/ts-sdk/src/credential.ts @@ -0,0 +1,157 @@ +// Code generated by ambient-sdk-generator from openapi.yaml — DO NOT EDIT. +// Source: ../../ambient-api-server/openapi/openapi.yaml +// Spec SHA256: c9d4494778eb0a006db1f289630460fb9b5dc58df1895903c42735b4f56b0314 +// Generated: 2026-05-05T15:50:22Z + +import type { ObjectReference, ListMeta } from './base'; + +export type Credential = ObjectReference & { + annotations: string; + description: string; + email: string; + labels: string; + name: string; + project_id: string; + provider: string; + token: string; + url: string; +}; + +export type CredentialList = ListMeta & { + items: Credential[]; +}; + +export type CredentialCreateRequest = { + annotations?: string; + description?: string; + email?: string; + labels?: string; + name: string; + project_id: string; + provider: string; + token?: string; + url?: string; +}; + +export type CredentialPatchRequest = { + annotations?: string; + description?: string; + email?: string; + labels?: string; + name?: string; + provider?: string; + token?: string; + url?: string; +}; + +export class CredentialBuilder { + private data: Record = {}; + + + annotations(value: string): this { + this.data['annotations'] = value; + return this; + } + + description(value: string): this { + this.data['description'] = value; + return this; + } + + email(value: string): this { + this.data['email'] = value; + return this; + } + + labels(value: string): this { + this.data['labels'] = value; + return this; + } + + name(value: string): this { + this.data['name'] = value; + return this; + } + + projectId(value: string): this { + this.data['project_id'] = value; + return this; + } + + provider(value: string): this { + this.data['provider'] = value; + return this; + } + + token(value: string): this { + this.data['token'] = value; + return this; + } + + url(value: string): this { + this.data['url'] = value; + return this; + } + + build(): CredentialCreateRequest { + if (!this.data['name']) { + throw new Error('name is required'); + } + if (!this.data['project_id']) { + throw new Error('project_id is required'); + } + if (!this.data['provider']) { + throw new Error('provider is required'); + } + return this.data as CredentialCreateRequest; + } +} + +export class CredentialPatchBuilder { + private data: Record = {}; + + + annotations(value: string): this { + this.data['annotations'] = value; + return this; + } + + description(value: string): this { + this.data['description'] = value; + return this; + } + + email(value: string): this { + this.data['email'] = value; + return this; + } + + labels(value: string): this { + this.data['labels'] = value; + return this; + } + + name(value: string): this { + this.data['name'] = value; + return this; + } + + provider(value: string): this { + this.data['provider'] = value; + return this; + } + + token(value: string): this { + this.data['token'] = value; + return this; + } + + url(value: string): this { + this.data['url'] = value; + return this; + } + + build(): CredentialPatchRequest { + return this.data as CredentialPatchRequest; + } +} diff --git a/components/ambient-sdk/ts-sdk/src/credential_api.ts b/components/ambient-sdk/ts-sdk/src/credential_api.ts new file mode 100644 index 000000000..a48144302 --- /dev/null +++ b/components/ambient-sdk/ts-sdk/src/credential_api.ts @@ -0,0 +1,53 @@ +// Code generated by ambient-sdk-generator from openapi.yaml — DO NOT EDIT. +// Source: ../../ambient-api-server/openapi/openapi.yaml +// Spec SHA256: c9d4494778eb0a006db1f289630460fb9b5dc58df1895903c42735b4f56b0314 +// Generated: 2026-05-05T15:50:22Z + +import type { AmbientClientConfig, ListOptions, RequestOptions } from './base'; +import { ambientFetch, buildQueryString } from './base'; +import type { Credential, CredentialList, CredentialCreateRequest, CredentialPatchRequest } from './credential'; + +export class CredentialAPI { + constructor(private readonly config: AmbientClientConfig) {} + private basePath(): string { + if (!this.config.project) { + throw new Error('project is required for Credential operations'); + } + return '/projects/{id}/credentials'.replace('{id}', encodeURIComponent(this.config.project)); + } + + + async create(data: CredentialCreateRequest, opts?: RequestOptions): Promise { + return ambientFetch(this.config, 'POST', this.basePath(), data, opts); + } + + async get(id: string, opts?: RequestOptions): Promise { + return ambientFetch(this.config, 'GET', `${this.basePath()}/${id}`, undefined, opts); + } + + async list(listOpts?: ListOptions, opts?: RequestOptions): Promise { + const qs = buildQueryString(listOpts); + return ambientFetch(this.config, 'GET', `${this.basePath()}${qs}`, undefined, opts); + } + async update(id: string, patch: CredentialPatchRequest, opts?: RequestOptions): Promise { + return ambientFetch(this.config, 'PATCH', `${this.basePath()}/${id}`, patch, opts); + } + + async delete(id: string, opts?: RequestOptions): Promise { + return ambientFetch(this.config, 'DELETE', `${this.basePath()}/${id}`, undefined, opts); + } + + async *listAll(size: number = 100, opts?: RequestOptions): AsyncGenerator { + let page = 1; + while (true) { + const result = await this.list({ page, size }, opts); + for (const item of result.items) { + yield item; + } + if (page * size >= result.total) { + break; + } + page++; + } + } +} diff --git a/components/ambient-sdk/ts-sdk/src/inbox_message.ts b/components/ambient-sdk/ts-sdk/src/inbox_message.ts index 59a119948..1ab918082 100644 --- a/components/ambient-sdk/ts-sdk/src/inbox_message.ts +++ b/components/ambient-sdk/ts-sdk/src/inbox_message.ts @@ -1,7 +1,7 @@ // Code generated by ambient-sdk-generator from openapi.yaml — DO NOT EDIT. // Source: ../../ambient-api-server/openapi/openapi.yaml -// Spec SHA256: 97773951f427a0271532ec5ed2e18c6441b246989b0aebbe755b544b47e2e702 -// Generated: 2026-04-18T21:11:11Z +// Spec SHA256: c9d4494778eb0a006db1f289630460fb9b5dc58df1895903c42735b4f56b0314 +// Generated: 2026-05-05T15:50:22Z import type { ObjectReference, ListMeta } from './base'; diff --git a/components/ambient-sdk/ts-sdk/src/inbox_message_api.ts b/components/ambient-sdk/ts-sdk/src/inbox_message_api.ts index dce744c32..4d8f13d11 100644 --- a/components/ambient-sdk/ts-sdk/src/inbox_message_api.ts +++ b/components/ambient-sdk/ts-sdk/src/inbox_message_api.ts @@ -1,26 +1,53 @@ -import type { AmbientClientConfig, ListOptions, RequestOptions, ListMeta } from './base'; -import { ambientFetch, buildQueryString } from './base'; -import type { InboxMessage, InboxMessageCreateRequest } from './inbox_message'; +// Code generated by ambient-sdk-generator from openapi.yaml — DO NOT EDIT. +// Source: ../../ambient-api-server/openapi/openapi.yaml +// Spec SHA256: c9d4494778eb0a006db1f289630460fb9b5dc58df1895903c42735b4f56b0314 +// Generated: 2026-05-05T15:50:22Z -export type InboxMessageList = ListMeta & { items: InboxMessage[] }; +import type { AmbientClientConfig, ListOptions, RequestOptions } from './base'; +import { ambientFetch, buildQueryString } from './base'; +import type { InboxMessage, InboxMessageList, InboxMessageCreateRequest, InboxMessagePatchRequest } from './inbox_message'; export class InboxMessageAPI { constructor(private readonly config: AmbientClientConfig) {} + private basePath(): string { + if (!this.config.project) { + throw new Error('project is required for InboxMessage operations'); + } + return '/projects/{id}/agents/{agent_id}/inbox'.replace('{id}', encodeURIComponent(this.config.project)); + } + - async send(projectId: string, agentId: string, data: InboxMessageCreateRequest, opts?: RequestOptions): Promise { - return ambientFetch(this.config, 'POST', `/projects/${encodeURIComponent(projectId)}/agents/${encodeURIComponent(agentId)}/inbox`, data, opts); + async create(data: InboxMessageCreateRequest, opts?: RequestOptions): Promise { + return ambientFetch(this.config, 'POST', this.basePath(), data, opts); } - async listByAgent(projectId: string, agentId: string, listOpts?: ListOptions, opts?: RequestOptions): Promise { + async get(id: string, opts?: RequestOptions): Promise { + return ambientFetch(this.config, 'GET', `${this.basePath()}/${id}`, undefined, opts); + } + + async list(listOpts?: ListOptions, opts?: RequestOptions): Promise { const qs = buildQueryString(listOpts); - return ambientFetch(this.config, 'GET', `/projects/${encodeURIComponent(projectId)}/agents/${encodeURIComponent(agentId)}/inbox${qs}`, undefined, opts); + return ambientFetch(this.config, 'GET', `${this.basePath()}${qs}`, undefined, opts); + } + async update(id: string, patch: InboxMessagePatchRequest, opts?: RequestOptions): Promise { + return ambientFetch(this.config, 'PATCH', `${this.basePath()}/${id}`, patch, opts); } - async markRead(projectId: string, agentId: string, msgId: string, opts?: RequestOptions): Promise { - return ambientFetch(this.config, 'PATCH', `/projects/${encodeURIComponent(projectId)}/agents/${encodeURIComponent(agentId)}/inbox/${encodeURIComponent(msgId)}`, { read: true }, opts); + async delete(id: string, opts?: RequestOptions): Promise { + return ambientFetch(this.config, 'DELETE', `${this.basePath()}/${id}`, undefined, opts); } - async deleteMessage(projectId: string, agentId: string, msgId: string, opts?: RequestOptions): Promise { - return ambientFetch(this.config, 'DELETE', `/projects/${encodeURIComponent(projectId)}/agents/${encodeURIComponent(agentId)}/inbox/${encodeURIComponent(msgId)}`, undefined, opts); + async *listAll(size: number = 100, opts?: RequestOptions): AsyncGenerator { + let page = 1; + while (true) { + const result = await this.list({ page, size }, opts); + for (const item of result.items) { + yield item; + } + if (page * size >= result.total) { + break; + } + page++; + } } } diff --git a/components/ambient-sdk/ts-sdk/src/index.ts b/components/ambient-sdk/ts-sdk/src/index.ts index 73f97681d..e13107292 100644 --- a/components/ambient-sdk/ts-sdk/src/index.ts +++ b/components/ambient-sdk/ts-sdk/src/index.ts @@ -1,26 +1,28 @@ // Code generated by ambient-sdk-generator from openapi.yaml — DO NOT EDIT. // Source: ../../ambient-api-server/openapi/openapi.yaml -// Spec SHA256: 9a8e623edcfae33acf56edf974d1859a127c22915d4831cb786daba2b398ca37 -// Generated: 2026-03-21T21:30:53Z +// Spec SHA256: c9d4494778eb0a006db1f289630460fb9b5dc58df1895903c42735b4f56b0314 +// Generated: 2026-05-05T15:50:22Z export { AmbientClient } from './client'; export type { AmbientClientConfig, ListOptions, RequestOptions, ObjectReference, ListMeta, APIError } from './base'; export { AmbientAPIError, buildQueryString } from './base'; -export type { InboxMessage, InboxMessageCreateRequest, InboxMessagePatchRequest } from './inbox_message'; +export type { Agent, AgentList, AgentCreateRequest, AgentPatchRequest } from './agent'; +export { AgentBuilder, AgentPatchBuilder } from './agent'; +export { AgentAPI } from './agent_api'; + +export type { Credential, CredentialList, CredentialCreateRequest, CredentialPatchRequest } from './credential'; +export { CredentialBuilder, CredentialPatchBuilder } from './credential'; +export { CredentialAPI } from './credential_api'; + +export type { InboxMessage, InboxMessageList, InboxMessageCreateRequest, InboxMessagePatchRequest } from './inbox_message'; export { InboxMessageBuilder, InboxMessagePatchBuilder } from './inbox_message'; export { InboxMessageAPI } from './inbox_message_api'; -export type { InboxMessageList } from './inbox_message_api'; export type { Project, ProjectList, ProjectCreateRequest, ProjectPatchRequest } from './project'; export { ProjectBuilder, ProjectPatchBuilder } from './project'; export { ProjectAPI } from './project_api'; -export type { Agent, AgentList, AgentCreateRequest, AgentPatchRequest } from './agent'; -export { AgentBuilder, AgentPatchBuilder } from './agent'; -export { AgentAPI } from './agent_api'; -export type { AgentSessionList, StartRequest, StartResponse } from './agent_api'; - export type { ProjectSettings, ProjectSettingsList, ProjectSettingsCreateRequest, ProjectSettingsPatchRequest } from './project_settings'; export { ProjectSettingsBuilder, ProjectSettingsPatchBuilder } from './project_settings'; export { ProjectSettingsAPI } from './project_settings_api'; @@ -33,6 +35,10 @@ export type { RoleBinding, RoleBindingList, RoleBindingCreateRequest, RoleBindin export { RoleBindingBuilder, RoleBindingPatchBuilder } from './role_binding'; export { RoleBindingAPI } from './role_binding_api'; +export type { ScheduledSession, ScheduledSessionList, ScheduledSessionCreateRequest, ScheduledSessionPatchRequest } from './scheduled_session'; +export { ScheduledSessionBuilder, ScheduledSessionPatchBuilder } from './scheduled_session'; +export { ScheduledSessionAPI } from './scheduled_session_api'; + export type { Session, SessionList, SessionCreateRequest, SessionPatchRequest, SessionStatusPatchRequest } from './session'; export { SessionBuilder, SessionPatchBuilder, SessionStatusPatchBuilder } from './session'; export { SessionAPI } from './session_api'; diff --git a/components/ambient-sdk/ts-sdk/src/project.ts b/components/ambient-sdk/ts-sdk/src/project.ts index 6a999a583..03eed06f3 100644 --- a/components/ambient-sdk/ts-sdk/src/project.ts +++ b/components/ambient-sdk/ts-sdk/src/project.ts @@ -1,13 +1,14 @@ // Code generated by ambient-sdk-generator from openapi.yaml — DO NOT EDIT. // Source: ../../ambient-api-server/openapi/openapi.yaml -// Spec SHA256: 9a8e623edcfae33acf56edf974d1859a127c22915d4831cb786daba2b398ca37 -// Generated: 2026-03-21T21:30:53Z +// Spec SHA256: c9d4494778eb0a006db1f289630460fb9b5dc58df1895903c42735b4f56b0314 +// Generated: 2026-05-05T15:50:22Z import type { ObjectReference, ListMeta } from './base'; export type Project = ObjectReference & { annotations: string; description: string; + display_name: string; labels: string; name: string; prompt: string; @@ -21,6 +22,7 @@ export type ProjectList = ListMeta & { export type ProjectCreateRequest = { annotations?: string; description?: string; + display_name?: string; labels?: string; name: string; prompt?: string; @@ -30,6 +32,7 @@ export type ProjectCreateRequest = { export type ProjectPatchRequest = { annotations?: string; description?: string; + display_name?: string; labels?: string; name?: string; prompt?: string; @@ -50,6 +53,11 @@ export class ProjectBuilder { return this; } + displayName(value: string): this { + this.data['display_name'] = value; + return this; + } + labels(value: string): this { this.data['labels'] = value; return this; @@ -92,6 +100,11 @@ export class ProjectPatchBuilder { return this; } + displayName(value: string): this { + this.data['display_name'] = value; + return this; + } + labels(value: string): this { this.data['labels'] = value; return this; diff --git a/components/ambient-sdk/ts-sdk/src/project_api.ts b/components/ambient-sdk/ts-sdk/src/project_api.ts index 2fc1dc0fc..3a62d9cc7 100644 --- a/components/ambient-sdk/ts-sdk/src/project_api.ts +++ b/components/ambient-sdk/ts-sdk/src/project_api.ts @@ -1,7 +1,7 @@ // Code generated by ambient-sdk-generator from openapi.yaml — DO NOT EDIT. // Source: ../../ambient-api-server/openapi/openapi.yaml -// Spec SHA256: 97773951f427a0271532ec5ed2e18c6441b246989b0aebbe755b544b47e2e702 -// Generated: 2026-04-18T21:11:11Z +// Spec SHA256: c9d4494778eb0a006db1f289630460fb9b5dc58df1895903c42735b4f56b0314 +// Generated: 2026-05-05T15:50:22Z import type { AmbientClientConfig, ListOptions, RequestOptions } from './base'; import { ambientFetch, buildQueryString } from './base'; diff --git a/components/ambient-sdk/ts-sdk/src/project_settings.ts b/components/ambient-sdk/ts-sdk/src/project_settings.ts index d231fa40f..19b354bad 100644 --- a/components/ambient-sdk/ts-sdk/src/project_settings.ts +++ b/components/ambient-sdk/ts-sdk/src/project_settings.ts @@ -1,7 +1,7 @@ // Code generated by ambient-sdk-generator from openapi.yaml — DO NOT EDIT. // Source: ../../ambient-api-server/openapi/openapi.yaml -// Spec SHA256: 97773951f427a0271532ec5ed2e18c6441b246989b0aebbe755b544b47e2e702 -// Generated: 2026-04-18T21:11:11Z +// Spec SHA256: c9d4494778eb0a006db1f289630460fb9b5dc58df1895903c42735b4f56b0314 +// Generated: 2026-05-05T15:50:22Z import type { ObjectReference, ListMeta } from './base'; diff --git a/components/ambient-sdk/ts-sdk/src/project_settings_api.ts b/components/ambient-sdk/ts-sdk/src/project_settings_api.ts index 11cae5345..a71c3fa80 100644 --- a/components/ambient-sdk/ts-sdk/src/project_settings_api.ts +++ b/components/ambient-sdk/ts-sdk/src/project_settings_api.ts @@ -1,7 +1,7 @@ // Code generated by ambient-sdk-generator from openapi.yaml — DO NOT EDIT. // Source: ../../ambient-api-server/openapi/openapi.yaml -// Spec SHA256: 97773951f427a0271532ec5ed2e18c6441b246989b0aebbe755b544b47e2e702 -// Generated: 2026-04-18T21:11:11Z +// Spec SHA256: c9d4494778eb0a006db1f289630460fb9b5dc58df1895903c42735b4f56b0314 +// Generated: 2026-05-05T15:50:22Z import type { AmbientClientConfig, ListOptions, RequestOptions } from './base'; import { ambientFetch, buildQueryString } from './base'; diff --git a/components/ambient-sdk/ts-sdk/src/role.ts b/components/ambient-sdk/ts-sdk/src/role.ts index b6952464a..8c038e975 100644 --- a/components/ambient-sdk/ts-sdk/src/role.ts +++ b/components/ambient-sdk/ts-sdk/src/role.ts @@ -1,7 +1,7 @@ // Code generated by ambient-sdk-generator from openapi.yaml — DO NOT EDIT. // Source: ../../ambient-api-server/openapi/openapi.yaml -// Spec SHA256: 97773951f427a0271532ec5ed2e18c6441b246989b0aebbe755b544b47e2e702 -// Generated: 2026-04-18T21:11:11Z +// Spec SHA256: c9d4494778eb0a006db1f289630460fb9b5dc58df1895903c42735b4f56b0314 +// Generated: 2026-05-05T15:50:22Z import type { ObjectReference, ListMeta } from './base'; diff --git a/components/ambient-sdk/ts-sdk/src/role_api.ts b/components/ambient-sdk/ts-sdk/src/role_api.ts index b5350904f..b5f52b3bc 100644 --- a/components/ambient-sdk/ts-sdk/src/role_api.ts +++ b/components/ambient-sdk/ts-sdk/src/role_api.ts @@ -1,7 +1,7 @@ // Code generated by ambient-sdk-generator from openapi.yaml — DO NOT EDIT. // Source: ../../ambient-api-server/openapi/openapi.yaml -// Spec SHA256: 9a8e623edcfae33acf56edf974d1859a127c22915d4831cb786daba2b398ca37 -// Generated: 2026-03-21T21:30:53Z +// Spec SHA256: c9d4494778eb0a006db1f289630460fb9b5dc58df1895903c42735b4f56b0314 +// Generated: 2026-05-05T15:50:22Z import type { AmbientClientConfig, ListOptions, RequestOptions } from './base'; import { ambientFetch, buildQueryString } from './base'; @@ -26,6 +26,10 @@ export class RoleAPI { return ambientFetch(this.config, 'PATCH', `/roles/${id}`, patch, opts); } + async delete(id: string, opts?: RequestOptions): Promise { + return ambientFetch(this.config, 'DELETE', `/roles/${id}`, undefined, opts); + } + async *listAll(size: number = 100, opts?: RequestOptions): AsyncGenerator { let page = 1; while (true) { diff --git a/components/ambient-sdk/ts-sdk/src/role_binding.ts b/components/ambient-sdk/ts-sdk/src/role_binding.ts index 5dcc33816..da6ec209e 100644 --- a/components/ambient-sdk/ts-sdk/src/role_binding.ts +++ b/components/ambient-sdk/ts-sdk/src/role_binding.ts @@ -1,7 +1,7 @@ // Code generated by ambient-sdk-generator from openapi.yaml — DO NOT EDIT. // Source: ../../ambient-api-server/openapi/openapi.yaml -// Spec SHA256: 97773951f427a0271532ec5ed2e18c6441b246989b0aebbe755b544b47e2e702 -// Generated: 2026-04-18T21:11:11Z +// Spec SHA256: c9d4494778eb0a006db1f289630460fb9b5dc58df1895903c42735b4f56b0314 +// Generated: 2026-05-05T15:50:22Z import type { ObjectReference, ListMeta } from './base'; diff --git a/components/ambient-sdk/ts-sdk/src/role_binding_api.ts b/components/ambient-sdk/ts-sdk/src/role_binding_api.ts index 94261dcb8..1023a2763 100644 --- a/components/ambient-sdk/ts-sdk/src/role_binding_api.ts +++ b/components/ambient-sdk/ts-sdk/src/role_binding_api.ts @@ -1,7 +1,7 @@ // Code generated by ambient-sdk-generator from openapi.yaml — DO NOT EDIT. // Source: ../../ambient-api-server/openapi/openapi.yaml -// Spec SHA256: 9a8e623edcfae33acf56edf974d1859a127c22915d4831cb786daba2b398ca37 -// Generated: 2026-03-21T21:30:53Z +// Spec SHA256: c9d4494778eb0a006db1f289630460fb9b5dc58df1895903c42735b4f56b0314 +// Generated: 2026-05-05T15:50:22Z import type { AmbientClientConfig, ListOptions, RequestOptions } from './base'; import { ambientFetch, buildQueryString } from './base'; @@ -26,6 +26,10 @@ export class RoleBindingAPI { return ambientFetch(this.config, 'PATCH', `/role_bindings/${id}`, patch, opts); } + async delete(id: string, opts?: RequestOptions): Promise { + return ambientFetch(this.config, 'DELETE', `/role_bindings/${id}`, undefined, opts); + } + async *listAll(size: number = 100, opts?: RequestOptions): AsyncGenerator { let page = 1; while (true) { diff --git a/components/ambient-sdk/ts-sdk/src/scheduled_session.ts b/components/ambient-sdk/ts-sdk/src/scheduled_session.ts new file mode 100644 index 000000000..fe3758097 --- /dev/null +++ b/components/ambient-sdk/ts-sdk/src/scheduled_session.ts @@ -0,0 +1,210 @@ +// Code generated by ambient-sdk-generator from openapi.yaml — DO NOT EDIT. +// Source: ../../ambient-api-server/openapi/openapi.yaml +// Spec SHA256: c9d4494778eb0a006db1f289630460fb9b5dc58df1895903c42735b4f56b0314 +// Generated: 2026-05-05T15:50:22Z + +import type { ObjectReference, ListMeta } from './base'; + +export type ScheduledSession = ObjectReference & { + agent_id: string; + description: string; + enabled: boolean; + inactivity_timeout: number; + last_run_at: string; + name: string; + next_run_at: string; + project_id: string; + runner_type: string; + schedule: string; + session_prompt: string; + stop_on_run_finished: boolean; + timeout: number; + timezone: string; +}; + +export type ScheduledSessionList = ListMeta & { + items: ScheduledSession[]; +}; + +export type ScheduledSessionCreateRequest = { + agent_id?: string; + description?: string; + enabled?: boolean; + inactivity_timeout?: number; + last_run_at?: string; + name: string; + next_run_at?: string; + project_id: string; + runner_type?: string; + schedule: string; + session_prompt?: string; + stop_on_run_finished?: boolean; + timeout?: number; + timezone?: string; +}; + +export type ScheduledSessionPatchRequest = { + agent_id?: string; + description?: string; + enabled?: boolean; + inactivity_timeout?: number; + name?: string; + runner_type?: string; + schedule?: string; + session_prompt?: string; + stop_on_run_finished?: boolean; + timeout?: number; + timezone?: string; +}; + +export class ScheduledSessionBuilder { + private data: Record = {}; + + + agentId(value: string): this { + this.data['agent_id'] = value; + return this; + } + + description(value: string): this { + this.data['description'] = value; + return this; + } + + enabled(value: boolean): this { + this.data['enabled'] = value; + return this; + } + + inactivityTimeout(value: number): this { + this.data['inactivity_timeout'] = value; + return this; + } + + lastRunAt(value: string): this { + this.data['last_run_at'] = value; + return this; + } + + name(value: string): this { + this.data['name'] = value; + return this; + } + + nextRunAt(value: string): this { + this.data['next_run_at'] = value; + return this; + } + + projectId(value: string): this { + this.data['project_id'] = value; + return this; + } + + runnerType(value: string): this { + this.data['runner_type'] = value; + return this; + } + + schedule(value: string): this { + this.data['schedule'] = value; + return this; + } + + sessionPrompt(value: string): this { + this.data['session_prompt'] = value; + return this; + } + + stopOnRunFinished(value: boolean): this { + this.data['stop_on_run_finished'] = value; + return this; + } + + timeout(value: number): this { + this.data['timeout'] = value; + return this; + } + + timezone(value: string): this { + this.data['timezone'] = value; + return this; + } + + build(): ScheduledSessionCreateRequest { + if (!this.data['name']) { + throw new Error('name is required'); + } + if (!this.data['project_id']) { + throw new Error('project_id is required'); + } + if (!this.data['schedule']) { + throw new Error('schedule is required'); + } + return this.data as ScheduledSessionCreateRequest; + } +} + +export class ScheduledSessionPatchBuilder { + private data: Record = {}; + + + agentId(value: string): this { + this.data['agent_id'] = value; + return this; + } + + description(value: string): this { + this.data['description'] = value; + return this; + } + + enabled(value: boolean): this { + this.data['enabled'] = value; + return this; + } + + inactivityTimeout(value: number): this { + this.data['inactivity_timeout'] = value; + return this; + } + + name(value: string): this { + this.data['name'] = value; + return this; + } + + runnerType(value: string): this { + this.data['runner_type'] = value; + return this; + } + + schedule(value: string): this { + this.data['schedule'] = value; + return this; + } + + sessionPrompt(value: string): this { + this.data['session_prompt'] = value; + return this; + } + + stopOnRunFinished(value: boolean): this { + this.data['stop_on_run_finished'] = value; + return this; + } + + timeout(value: number): this { + this.data['timeout'] = value; + return this; + } + + timezone(value: string): this { + this.data['timezone'] = value; + return this; + } + + build(): ScheduledSessionPatchRequest { + return this.data as ScheduledSessionPatchRequest; + } +} diff --git a/components/ambient-sdk/ts-sdk/src/scheduled_session_api.ts b/components/ambient-sdk/ts-sdk/src/scheduled_session_api.ts new file mode 100644 index 000000000..04c7093ec --- /dev/null +++ b/components/ambient-sdk/ts-sdk/src/scheduled_session_api.ts @@ -0,0 +1,53 @@ +// Code generated by ambient-sdk-generator from openapi.yaml — DO NOT EDIT. +// Source: ../../ambient-api-server/openapi/openapi.yaml +// Spec SHA256: c9d4494778eb0a006db1f289630460fb9b5dc58df1895903c42735b4f56b0314 +// Generated: 2026-05-05T15:50:22Z + +import type { AmbientClientConfig, ListOptions, RequestOptions } from './base'; +import { ambientFetch, buildQueryString } from './base'; +import type { ScheduledSession, ScheduledSessionList, ScheduledSessionCreateRequest, ScheduledSessionPatchRequest } from './scheduled_session'; + +export class ScheduledSessionAPI { + constructor(private readonly config: AmbientClientConfig) {} + private basePath(): string { + if (!this.config.project) { + throw new Error('project is required for ScheduledSession operations'); + } + return '/projects/{id}/scheduled-sessions'.replace('{id}', encodeURIComponent(this.config.project)); + } + + + async create(data: ScheduledSessionCreateRequest, opts?: RequestOptions): Promise { + return ambientFetch(this.config, 'POST', this.basePath(), data, opts); + } + + async get(id: string, opts?: RequestOptions): Promise { + return ambientFetch(this.config, 'GET', `${this.basePath()}/${id}`, undefined, opts); + } + + async list(listOpts?: ListOptions, opts?: RequestOptions): Promise { + const qs = buildQueryString(listOpts); + return ambientFetch(this.config, 'GET', `${this.basePath()}${qs}`, undefined, opts); + } + async update(id: string, patch: ScheduledSessionPatchRequest, opts?: RequestOptions): Promise { + return ambientFetch(this.config, 'PATCH', `${this.basePath()}/${id}`, patch, opts); + } + + async delete(id: string, opts?: RequestOptions): Promise { + return ambientFetch(this.config, 'DELETE', `${this.basePath()}/${id}`, undefined, opts); + } + + async *listAll(size: number = 100, opts?: RequestOptions): AsyncGenerator { + let page = 1; + while (true) { + const result = await this.list({ page, size }, opts); + for (const item of result.items) { + yield item; + } + if (page * size >= result.total) { + break; + } + page++; + } + } +} diff --git a/components/ambient-sdk/ts-sdk/src/session.ts b/components/ambient-sdk/ts-sdk/src/session.ts index 626a80f2f..99a98472a 100644 --- a/components/ambient-sdk/ts-sdk/src/session.ts +++ b/components/ambient-sdk/ts-sdk/src/session.ts @@ -1,7 +1,7 @@ // Code generated by ambient-sdk-generator from openapi.yaml — DO NOT EDIT. // Source: ../../ambient-api-server/openapi/openapi.yaml -// Spec SHA256: 97773951f427a0271532ec5ed2e18c6441b246989b0aebbe755b544b47e2e702 -// Generated: 2026-04-18T21:11:11Z +// Spec SHA256: c9d4494778eb0a006db1f289630460fb9b5dc58df1895903c42735b4f56b0314 +// Generated: 2026-05-05T15:50:22Z import type { ObjectReference, ListMeta } from './base'; diff --git a/components/ambient-sdk/ts-sdk/src/session_api.ts b/components/ambient-sdk/ts-sdk/src/session_api.ts index ce29e8c01..16d159ddb 100644 --- a/components/ambient-sdk/ts-sdk/src/session_api.ts +++ b/components/ambient-sdk/ts-sdk/src/session_api.ts @@ -1,7 +1,7 @@ // Code generated by ambient-sdk-generator from openapi.yaml — DO NOT EDIT. // Source: ../../ambient-api-server/openapi/openapi.yaml -// Spec SHA256: 97773951f427a0271532ec5ed2e18c6441b246989b0aebbe755b544b47e2e702 -// Generated: 2026-04-18T21:11:11Z +// Spec SHA256: c9d4494778eb0a006db1f289630460fb9b5dc58df1895903c42735b4f56b0314 +// Generated: 2026-05-05T15:50:22Z import type { AmbientClientConfig, ListOptions, RequestOptions } from './base'; import { ambientFetch, buildQueryString } from './base'; diff --git a/components/ambient-sdk/ts-sdk/src/session_message.ts b/components/ambient-sdk/ts-sdk/src/session_message.ts index 14bfcef6c..26e97e530 100644 --- a/components/ambient-sdk/ts-sdk/src/session_message.ts +++ b/components/ambient-sdk/ts-sdk/src/session_message.ts @@ -1,7 +1,7 @@ // Code generated by ambient-sdk-generator from openapi.yaml — DO NOT EDIT. // Source: ../../ambient-api-server/openapi/openapi.yaml -// Spec SHA256: 97773951f427a0271532ec5ed2e18c6441b246989b0aebbe755b544b47e2e702 -// Generated: 2026-04-18T21:11:11Z +// Spec SHA256: c9d4494778eb0a006db1f289630460fb9b5dc58df1895903c42735b4f56b0314 +// Generated: 2026-05-05T15:50:22Z import type { ObjectReference, ListMeta } from './base'; diff --git a/components/ambient-sdk/ts-sdk/src/session_message_api.ts b/components/ambient-sdk/ts-sdk/src/session_message_api.ts index f81a74647..39c2d2a4d 100644 --- a/components/ambient-sdk/ts-sdk/src/session_message_api.ts +++ b/components/ambient-sdk/ts-sdk/src/session_message_api.ts @@ -1,7 +1,7 @@ // Code generated by ambient-sdk-generator from openapi.yaml — DO NOT EDIT. // Source: ../../ambient-api-server/openapi/openapi.yaml -// Spec SHA256: 9a8e623edcfae33acf56edf974d1859a127c22915d4831cb786daba2b398ca37 -// Generated: 2026-03-21T21:30:53Z +// Spec SHA256: c9d4494778eb0a006db1f289630460fb9b5dc58df1895903c42735b4f56b0314 +// Generated: 2026-05-05T15:50:22Z import type { AmbientClientConfig, ListOptions, RequestOptions } from './base'; import { ambientFetch, buildQueryString } from './base'; @@ -9,18 +9,25 @@ import type { SessionMessage, SessionMessageList, SessionMessageCreateRequest } export class SessionMessageAPI { constructor(private readonly config: AmbientClientConfig) {} + private basePath(): string { + if (!this.config.project) { + throw new Error('project is required for SessionMessage operations'); + } + return '/sessions/{id}/messages'.replace('{id}', encodeURIComponent(this.config.project)); + } + async create(data: SessionMessageCreateRequest, opts?: RequestOptions): Promise { - return ambientFetch(this.config, 'POST', '/sessions', data, opts); + return ambientFetch(this.config, 'POST', this.basePath(), data, opts); } async get(id: string, opts?: RequestOptions): Promise { - return ambientFetch(this.config, 'GET', `/sessions/${id}`, undefined, opts); + return ambientFetch(this.config, 'GET', `${this.basePath()}/${id}`, undefined, opts); } async list(listOpts?: ListOptions, opts?: RequestOptions): Promise { const qs = buildQueryString(listOpts); - return ambientFetch(this.config, 'GET', `/sessions${qs}`, undefined, opts); + return ambientFetch(this.config, 'GET', `${this.basePath()}${qs}`, undefined, opts); } async *listAll(size: number = 100, opts?: RequestOptions): AsyncGenerator { let page = 1; diff --git a/components/ambient-sdk/ts-sdk/src/user.ts b/components/ambient-sdk/ts-sdk/src/user.ts index 58648586f..24743568e 100644 --- a/components/ambient-sdk/ts-sdk/src/user.ts +++ b/components/ambient-sdk/ts-sdk/src/user.ts @@ -1,7 +1,7 @@ // Code generated by ambient-sdk-generator from openapi.yaml — DO NOT EDIT. // Source: ../../ambient-api-server/openapi/openapi.yaml -// Spec SHA256: 97773951f427a0271532ec5ed2e18c6441b246989b0aebbe755b544b47e2e702 -// Generated: 2026-04-18T21:11:11Z +// Spec SHA256: c9d4494778eb0a006db1f289630460fb9b5dc58df1895903c42735b4f56b0314 +// Generated: 2026-05-05T15:50:22Z import type { ObjectReference, ListMeta } from './base'; diff --git a/components/ambient-sdk/ts-sdk/src/user_api.ts b/components/ambient-sdk/ts-sdk/src/user_api.ts index 564ad1cd3..eceb97ee8 100644 --- a/components/ambient-sdk/ts-sdk/src/user_api.ts +++ b/components/ambient-sdk/ts-sdk/src/user_api.ts @@ -1,7 +1,7 @@ // Code generated by ambient-sdk-generator from openapi.yaml — DO NOT EDIT. // Source: ../../ambient-api-server/openapi/openapi.yaml -// Spec SHA256: 97773951f427a0271532ec5ed2e18c6441b246989b0aebbe755b544b47e2e702 -// Generated: 2026-04-18T21:11:11Z +// Spec SHA256: c9d4494778eb0a006db1f289630460fb9b5dc58df1895903c42735b4f56b0314 +// Generated: 2026-05-05T15:50:22Z import type { AmbientClientConfig, ListOptions, RequestOptions } from './base'; import { ambientFetch, buildQueryString } from './base'; diff --git a/components/ambient-sdk/ts-sdk/tests/client.test.ts b/components/ambient-sdk/ts-sdk/tests/client.test.ts index 77dd509a3..587cf4279 100644 --- a/components/ambient-sdk/ts-sdk/tests/client.test.ts +++ b/components/ambient-sdk/ts-sdk/tests/client.test.ts @@ -22,20 +22,31 @@ describe('AmbientClient construction', () => { })).toThrow('baseUrl is required'); }); - it('throws when token is missing', () => { - expect(() => new AmbientClient({ + it('creates client without token (browser mode)', () => { + const client = new AmbientClient({ baseUrl: 'https://api.ambient-platform.com', - token: '', - project: 'test-project', - })).toThrow('token is required'); + }); + expect(client).toBeDefined(); + expect(client.sessions).toBeDefined(); }); - it('throws when project is missing', () => { - expect(() => new AmbientClient({ + it('creates client without project', () => { + const client = new AmbientClient({ baseUrl: 'https://api.ambient-platform.com', token: 'sha256~abcdefghijklmnopqrstuvwxyz1234567890', - project: '', - })).toThrow('project is required'); + }); + expect(client).toBeDefined(); + expect(client.sessions).toBeDefined(); + }); + + it('creates client with relative baseUrl', () => { + const client = new AmbientClient({ + baseUrl: '/api/proxy', + token: 'sha256~abcdefghijklmnopqrstuvwxyz1234567890', + project: 'test-project', + }); + expect(client).toBeDefined(); + expect(client.sessions).toBeDefined(); }); it('strips trailing slashes from baseUrl', () => { @@ -46,6 +57,14 @@ describe('AmbientClient construction', () => { }); expect(client).toBeDefined(); }); + + it('rejects protocol-relative URLs', () => { + expect(() => new AmbientClient({ + baseUrl: '//evil.com/path', + token: 'sha256~abcdefghijklmnopqrstuvwxyz1234567890', + project: 'test-project', + })).toThrow('Protocol-relative URLs are not allowed for baseUrl'); + }); }); describe('AmbientClient.fromEnv', () => { @@ -61,7 +80,7 @@ describe('AmbientClient.fromEnv', () => { it('creates client from environment variables', () => { process.env.AMBIENT_API_URL = 'https://api.test.com'; - process.env.AMBIENT_TOKEN = 'sha256~testtoken123'; + process.env.AMBIENT_TOKEN = 'sha256~abcdefghijklmnopqrstuvwxyz1234567890'; process.env.AMBIENT_PROJECT = 'my-project'; const client = AmbientClient.fromEnv(); expect(client).toBeDefined(); @@ -74,10 +93,12 @@ describe('AmbientClient.fromEnv', () => { expect(() => AmbientClient.fromEnv()).toThrow('AMBIENT_TOKEN environment variable is required'); }); - it('throws when AMBIENT_PROJECT is missing', () => { + it('creates client when AMBIENT_PROJECT is missing', () => { process.env.AMBIENT_TOKEN = 'sha256~abcdefghijklmnopqrstuvwxyz1234567890'; delete process.env.AMBIENT_PROJECT; - expect(() => AmbientClient.fromEnv()).toThrow('AMBIENT_PROJECT environment variable is required'); + const client = AmbientClient.fromEnv(); + expect(client).toBeDefined(); + expect(client.sessions).toBeDefined(); }); });