Search by

jaguarjack / workbuddy-openapi

JaguarJack

workbuddy openapi PHP SDK

Package info

github.com/JaguarJack/workbuddy-openapi

pkg:composer/jaguarjack/workbuddy-openapi

Statistics

Installs: 4

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.0 2026-09-02 14:29 UTC

This package is auto-updated.

Last update: 2026-09-04 15:10:24 UTC


README

WorkBuddy OpenAPI 的 PHP SDK。公开接口采用显式静态 Facade,每个动作由独立 Action 类实现。

当前状态:REST SDK 与 ACP 实时通道已实现。公开 API 与协议风险见 架构文档

安装

composer install

项目发布后,使用方通过 Composer 引入:

composer require jaguarjack/workbuddy-openapi

面向 AI Agent 的使用约定

AI Agent 在操作本仓库或使用本 SDK 时遵循以下顺序:

  1. 读取本 README 与 docs/architecture.md
  2. 单用户流程使用 WorkBuddy::configure();多用户流程使用 WorkBuddy::using()withTokens()
  3. 仅调用 README 接口表中列出的公开方法。
  4. 使用具名参数,让调用意图保持清晰。
  5. 捕获 WorkBuddyException;通过 ErrorCode、HTTP 状态和 requestId 决定后续动作。
  6. sendLocalAssistantMessage、权限响应、ACP prompt、createTaskredeemCoderedeemGift 会产生外部副作用,执行前取得用户确认。
  7. 凭证只从环境变量或服务端密钥存储读取;日志统一脱敏。
  8. 创建任务与发送消息遇到网络超时时,先查询资源状态;兑换请求复用同一个 requestId

目标用法

use Workbuddy\Openapi\Config;
use Workbuddy\Openapi\Enums\ErrorCode;
use Workbuddy\Openapi\Exceptions\ApiException;
use Workbuddy\Openapi\WorkBuddy;

WorkBuddy::configure(Config::fromEnvironment());

try {
    $task = WorkBuddy::createTask(
        prompt: '整理今天的会议纪要',
        name: '会议纪要',
    );

    $task = WorkBuddy::getTask(taskId: $task->id);
} catch (ApiException $exception) {
    if ($exception->errorCode === ErrorCode::InsufficientScope) {
        // 引导用户补充授权范围。
    }

    // requestId 可用于服务端问题排查。
    report($exception->requestId);
}

实例模式

多用户、队列 Worker、Swoole 和 RoadRunner 使用独立实例:

$app = WorkBuddy::using(new Config(
    clientId: $clientId,
    clientSecret: $clientSecret,
    redirectUri: $redirectUri,
));

$alice = $app->withTokens($aliceTokens);
$bob = $app->withTokens($bobTokens);

$aliceTasks = $alice->listTasks();
$bobTasks = $bob->listTasks();

应用配置只创建一次。每次 withTokens() 返回新的 WorkBuddyClient,复用 Symfony HTTP Client,并独立持有用户 Token、REST Transport 与 ACP Transport。传统单用户调用可继续使用静态 WorkBuddy::configure()

OAuth 回调可以直接衔接:

$tokens = $app->exchangeAuthorizationCode(
    code: $code,
    returnedState: $state,
    expectedState: $expectedState,
);

$userClient = $app->withTokens($tokens);

配置

WORKBUDDY_CLIENT_ID=app_xxx
WORKBUDDY_CLIENT_SECRET=sk_xxx
WORKBUDDY_REDIRECT_URI=https://example.com/callback
WORKBUDDY_ACCESS_TOKEN=xxx
WORKBUDDY_REFRESH_TOKEN=xxx

client_secret 适用于服务端应用。授权回调必须使用与开放平台登记值字节级一致的 redirect_uri

动作索引

客户端生命周期

方法 用途 返回
WorkBuddy::using(Config, ?HttpClientInterface) 创建独立应用客户端 WorkBuddyClient
WorkBuddy::configure(Config, ?HttpClientInterface) 配置静态默认客户端 void
$client->withTokens(TokenSet) 派生用户授权客户端 WorkBuddyClient
WorkBuddy::withTokens(TokenSet) 从静态默认客户端派生用户客户端 WorkBuddyClient

完整接口表

静态 WorkBuddy 与实例 WorkBuddyClient 提供相同业务方法。

方法 HTTP / 协议 Scope / 凭证 返回 副作用
getAuthorizationUrl(array $scopes = [], ?string $state = null) GET /authorize client_id AuthorizationRequest 打开授权流程
exchangeAuthorizationCode(string $code, string $returnedState, string $expectedState) POST /token form 应用凭证 TokenSet 创建 Token
refreshAccessToken(?string $refreshToken = null) POST /token form 应用凭证 TokenSet 轮换 Token
getProfile() GET /user/profile user.profile.readable Profile 只读
verifyPhone(string $phoneNumber) POST /user/phoneverification JSON user.contact.readable bool 只读校验
getLocalAssistantStatus() GET /localassistant user.localassistant.readable LocalAssistantStatus 只读
sendLocalAssistantMessage(string $content, MessageType $type) POST /localassistant/message user.localassistant.invokable SentMessage 触发本地执行
respondToLocalAssistantPermission(string $requestId, array $answers) POST /localassistant/message user.localassistant.invokable SentMessage 回答 AskQuestion
listLocalAssistantMessages(int $limit = 20, int $offset = 0, ?string $afterMessageId = null) GET /localassistant/message user.localassistant.readable MessagePage 只读
createTask(string $prompt, ?string $name = null) POST /tasks user.task.invokable Task 创建云任务
listTasks(int $page = 1, int $size = 20) GET /tasks user.task.readable TaskPage 只读
getTask(string $taskId) GET /tasks/{task_id} user.task.readable Task 只读
listArtifacts(string $taskId, ?ArtifactType $type = null, ?string $sessionId = null, int $startMs = 0, int $endMs = 0, int $limit = 0, int $offset = 0) GET {sandbox}/api/session/artifacts 任务 Token ArtifactPage 只读
downloadArtifact(string $taskId, Artifact $artifact) Artifact URL 签名 URL 或任务 Token string 下载文件
redeemCode(string $code, string $requestId) POST /redemptions user.credit.exchange Redemption 发放积分
redeemGift(string $giftKey, string $giftCode, string $requestId) POST /redemptions user.credit.exchange Redemption 发放积分
openAcpConnection(string $taskId) ACP GET SSE 任务 Token AcpConnection 打开长连接
initializeAcp(AcpConnection $connection) JSON-RPC initialize ACP 连接 array 协商协议
loadAcpSession(AcpConnection $connection, string $cwd = '/workspace', array $mcpServers = []) JSON-RPC session/load ACP 连接 array 加载会话
promptAcp(AcpConnection $connection, string|array $prompt) JSON-RPC session/prompt + SSE ACP 连接 Generator<AcpEvent> 执行 Prompt
respondToAcpPermission(AcpConnection $connection, string|int $requestId, array $result) JSON-RPC response ACP 连接 void 回复权限请求
respondToAcpRequest(AcpConnection $connection, string|int $requestId, array $result) JSON-RPC response ACP 连接 void 回复通用服务端请求
closeAcpConnection(AcpConnection $connection) 本地操作 ACP 连接 void 关闭长连接

返回模型

类型 公开字段
TokenSet accessToken, tokenType, refreshToken, expiresIn, scope, openId
Profile nickname, avatar
Message id, role, content, type, rawType, createdAt, attachments, metadata
Task id, status, rawStatus, name, link, token, expiresAt, sandboxLink, sandboxDataLink, createdAt, updatedAt
Artifact Entry 元数据、公共字段,以及 plan/tasks/media/overview 分类型字段
ArtifactPage sessionId, artifacts, total, returned, limit, offset, hasMore, filterType, startMs, endMs
AcpEvent type, id, method, params, result, error, stopReason, rawStopReason, usage, raw
AcpException errorCode, httpStatus, requestId, retryable, context

未知枚举值映射为 Unknown,原始值保留在对应 DTO 中。响应字段类型异常统一抛出 ApiException(ErrorCode::InvalidResponse)

Artifact 下载

$page = $userClient->listArtifacts(
    taskId: $taskId,
    type: ArtifactType::Media,
    limit: 50,
);

$bytes = $userClient->downloadArtifact($taskId, $page->artifacts[0]);

下载优先使用 Entry 的直接 url。该 URL 按签名地址访问;agent:/// fallback 使用任务 Token 请求 sandbox。

Token 刷新

$userClient = $userClient->withTokens(
    $userClient->refreshAccessToken(),
);

刷新响应包含新 refresh_token 时完成轮换;省略该字段时沿用当前值。Token 的持久化方式由接入应用决定。

官方契约说明

  • 手机验证端点表标注 GET,请求示例使用 POST JSON;SDK 采用可承载 phone_number 的 POST 示例。
  • Token scope 示例出现 task.write;SDK 的任务接口使用端点表定义的 user.task.readableuser.task.invokable
  • Task link 示例存在 /sessions/{id}/acp 两种形态;Artifact 优先按 /acp 规则派生地址,并兼容 sandboxLink
  • ACP 按 open → initialized → loaded → prompting → loaded → closed 校验调用顺序。

ACP 实时会话

use Workbuddy\Openapi\Enums\AcpEventType;

$connection = WorkBuddy::openAcpConnection(taskId: $task->id);

try {
    WorkBuddy::initializeAcp($connection);
    WorkBuddy::loadAcpSession($connection);

    foreach (WorkBuddy::promptAcp($connection, '继续整理结果') as $event) {
        if ($event->type === AcpEventType::PermissionRequest) {
            WorkBuddy::respondToAcpPermission(
                connection: $connection,
                requestId: $event->id,
                result: ['outcome' => 'approved'],
            );
        }

        if ($event->type === AcpEventType::FinalResponse) {
            $stopReason = $event->result['stopReason'] ?? null;
        }
    }
} finally {
    WorkBuddy::closeAcpConnection($connection);
}

promptAcp() 每条连接只运行一个活跃 prompt。调用方完整消费事件流,或在提前结束时关闭连接。传输中断会抛出 AcpException;prompt 已提交后的中断使用 ErrorCode::AcpOutcomeUnknown 表达结果未知。

错误处理

SDK 使用异常表达失败,使用枚举表达稳定错误语义:

try {
    $profile = WorkBuddy::getProfile();
} catch (ApiException $exception) {
    match ($exception->errorCode) {
        ErrorCode::InvalidToken => refreshTokenAndRetryOnce(),
        ErrorCode::InsufficientScope => requestRequiredScope(),
        ErrorCode::RateLimited => retryWithBackoff($exception->retryAfter),
        default => reportToWorkBuddy($exception->requestId),
    };
}

HTTP/API 错误尽量保留上游错误码、HTTP 状态、request_id 与脱敏上下文。SDK 本地校验和响应结构错误使用对应错误枚举,服务端元数据字段可能为空。完整层次见 错误模型

官方文档

开发验证

composer test
composer validate --strict

HTTP 客户端使用 Symfony HttpClient 6.4,最低支持 PHP 8.1。本地 PHPUnit 运行于 PHP 8.3,CI 覆盖 PHP 8.1–8.4。