mirror of
https://github.com/stablyai/orca.git
synced 2026-10-01 16:02:03 +00:00
refactor(mobile): let the task RPC modules take any unary sender
The hosted page has no socket client, so the modules the native app uses now take a sender that returns only what a unary caller reads. That is the same shape the existing Pick<RpcClient, 'sendRequest'> sites meant. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import type { GitHubPRMergeMethod } from '../../../src/shared/github/pull-request-types'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import type { RpcRequestSender } from '../transport/rpc-client'
|
||||
import { buildGithubPrParams, githubPrRepoSlugParam, type GitHubPrRepoSlug } from './github-pr-rpc'
|
||||
|
||||
// Mutation wrappers for the github.* PR surface, split out so github-pr-rpc.ts
|
||||
@@ -17,7 +17,7 @@ export type GitHubPrMutationOutcome = { ok: true; comment?: unknown } | { ok: fa
|
||||
type RawResult = { ok: true; result: unknown } | { ok: false; error: string }
|
||||
|
||||
async function sendRaw(
|
||||
client: Pick<RpcClient, 'sendRequest'>,
|
||||
client: RpcRequestSender,
|
||||
method: string,
|
||||
params: Record<string, unknown>
|
||||
): Promise<RawResult> {
|
||||
@@ -52,7 +52,7 @@ function extractMutationError(error: unknown, method: string): string {
|
||||
// `response.ok === false` (timeout/connection) is also a failure. Both collapse
|
||||
// into one outcome the action hook classifies via classifyPrSidebarFailure.
|
||||
async function sendGithubPrMutation(
|
||||
client: Pick<RpcClient, 'sendRequest'>,
|
||||
client: RpcRequestSender,
|
||||
method: string,
|
||||
params: Record<string, unknown>,
|
||||
options?: { timeoutMs?: number }
|
||||
@@ -83,7 +83,7 @@ async function sendGithubPrMutation(
|
||||
}
|
||||
|
||||
export async function fetchMergePR(
|
||||
client: Pick<RpcClient, 'sendRequest'>,
|
||||
client: RpcRequestSender,
|
||||
worktreeId: string,
|
||||
args: { prNumber: number; method?: GitHubPRMergeMethod; prRepo?: GitHubPrRepoSlug | null },
|
||||
options?: { timeoutMs?: number }
|
||||
@@ -104,7 +104,7 @@ export async function fetchMergePR(
|
||||
// which sendGithubPrMutation reads via its "no structured status" success branch
|
||||
// only when not boolean — so handle the boolean explicitly like resolveReviewThread.
|
||||
export async function fetchUpdatePRTitle(
|
||||
client: Pick<RpcClient, 'sendRequest'>,
|
||||
client: RpcRequestSender,
|
||||
worktreeId: string,
|
||||
args: { prNumber: number; title: string; prRepo?: GitHubPrRepoSlug | null }
|
||||
): Promise<GitHubPrMutationOutcome> {
|
||||
@@ -126,7 +126,7 @@ export async function fetchUpdatePRTitle(
|
||||
}
|
||||
|
||||
export async function fetchSetPRAutoMerge(
|
||||
client: Pick<RpcClient, 'sendRequest'>,
|
||||
client: RpcRequestSender,
|
||||
worktreeId: string,
|
||||
args: {
|
||||
prNumber: number
|
||||
@@ -147,7 +147,7 @@ export async function fetchSetPRAutoMerge(
|
||||
}
|
||||
|
||||
export async function fetchUpdatePRState(
|
||||
client: Pick<RpcClient, 'sendRequest'>,
|
||||
client: RpcRequestSender,
|
||||
worktreeId: string,
|
||||
args: { prNumber: number; state: 'open' | 'closed'; prRepo?: GitHubPrRepoSlug | null }
|
||||
): Promise<GitHubPrMutationOutcome> {
|
||||
@@ -164,7 +164,7 @@ export async function fetchUpdatePRState(
|
||||
}
|
||||
|
||||
export async function fetchRequestPRReviewers(
|
||||
client: Pick<RpcClient, 'sendRequest'>,
|
||||
client: RpcRequestSender,
|
||||
worktreeId: string,
|
||||
args: { prNumber: number; reviewers: string[]; prRepo?: GitHubPrRepoSlug | null }
|
||||
): Promise<GitHubPrMutationOutcome> {
|
||||
@@ -181,7 +181,7 @@ export async function fetchRequestPRReviewers(
|
||||
}
|
||||
|
||||
export async function fetchRemovePRReviewers(
|
||||
client: Pick<RpcClient, 'sendRequest'>,
|
||||
client: RpcRequestSender,
|
||||
worktreeId: string,
|
||||
args: { prNumber: number; reviewers: string[]; prRepo?: GitHubPrRepoSlug | null }
|
||||
): Promise<GitHubPrMutationOutcome> {
|
||||
@@ -201,7 +201,7 @@ export async function fetchRemovePRReviewers(
|
||||
// (`{ ok, comment } | { ok:false, error }`), which sendGithubPrMutation reads via
|
||||
// its `ok in result` branch. We refetch afterward, so the returned comment is unused.
|
||||
export async function fetchAddPRReviewCommentReply(
|
||||
client: Pick<RpcClient, 'sendRequest'>,
|
||||
client: RpcRequestSender,
|
||||
worktreeId: string,
|
||||
args: {
|
||||
prNumber: number
|
||||
@@ -238,7 +238,7 @@ export async function fetchAddPRReviewCommentReply(
|
||||
|
||||
// Add a root conversation comment to a PR or an issue. Host returns GitHubCommentResult.
|
||||
export async function fetchAddIssueComment(
|
||||
client: Pick<RpcClient, 'sendRequest'>,
|
||||
client: RpcRequestSender,
|
||||
worktreeId: string,
|
||||
args: {
|
||||
prNumber: number
|
||||
@@ -265,7 +265,7 @@ export async function fetchAddIssueComment(
|
||||
// the matching GraphQL mutation). Unlike the comment mutations, the host returns a
|
||||
// bare boolean, so a falsy result is a failure rather than the "no status" success.
|
||||
export async function fetchResolveReviewThread(
|
||||
client: Pick<RpcClient, 'sendRequest'>,
|
||||
client: RpcRequestSender,
|
||||
worktreeId: string,
|
||||
args: { threadId: string; resolve: boolean; prRepo?: GitHubPrRepoSlug | null }
|
||||
): Promise<GitHubPrMutationOutcome> {
|
||||
@@ -298,7 +298,7 @@ export async function fetchResolveReviewThread(
|
||||
// directly rather than via buildGithubPrParams. Host returns the
|
||||
// GitHubProjectMutationResult `{ ok }` envelope sendGithubPrMutation reads.
|
||||
export async function fetchUpdateIssueComment(
|
||||
client: Pick<RpcClient, 'sendRequest'>,
|
||||
client: RpcRequestSender,
|
||||
args: { owner: string; repo: string; host?: string; commentId: number; body: string }
|
||||
): Promise<GitHubPrMutationOutcome> {
|
||||
return sendGithubPrMutation(client, 'github.project.updateIssueCommentBySlug', {
|
||||
@@ -310,7 +310,7 @@ export async function fetchUpdateIssueComment(
|
||||
|
||||
// Delete a root conversation (issue) comment. Slug-addressed like the edit wrapper.
|
||||
export async function fetchDeleteIssueComment(
|
||||
client: Pick<RpcClient, 'sendRequest'>,
|
||||
client: RpcRequestSender,
|
||||
args: { owner: string; repo: string; host?: string; commentId: number }
|
||||
): Promise<GitHubPrMutationOutcome> {
|
||||
return sendGithubPrMutation(client, 'github.project.deleteIssueCommentBySlug', {
|
||||
@@ -320,7 +320,7 @@ export async function fetchDeleteIssueComment(
|
||||
}
|
||||
|
||||
export async function fetchRerunPRChecks(
|
||||
client: Pick<RpcClient, 'sendRequest'>,
|
||||
client: RpcRequestSender,
|
||||
worktreeId: string,
|
||||
args: {
|
||||
prNumber: number
|
||||
|
||||
@@ -5,8 +5,7 @@ import type {
|
||||
import type { MobileWebTaskLinearIssue } from '../../../src/shared/mobile-web/task-list-contract'
|
||||
import type { HostTaskDetailOperations } from './host-task-detail-operations'
|
||||
import { projectGitHubTaskDetail } from './github-task-detail-projection'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import type { RpcSuccess } from '../transport/types'
|
||||
import type { RpcRequestSender } from '../transport/rpc-client'
|
||||
|
||||
type GitLabRawDetails = Partial<MobileWebTaskGitLabDetailResult> & {
|
||||
item?: {
|
||||
@@ -15,7 +14,7 @@ type GitLabRawDetails = Partial<MobileWebTaskGitLabDetailResult> & {
|
||||
}
|
||||
}
|
||||
|
||||
export function nativeHostTaskDetailOperations(client: RpcClient): HostTaskDetailOperations {
|
||||
export function nativeHostTaskDetailOperations(client: RpcRequestSender): HostTaskDetailOperations {
|
||||
return {
|
||||
async listGitHubLabels(repoId) {
|
||||
return successfulResult(
|
||||
@@ -114,5 +113,5 @@ async function successfulResult<T>(request: Promise<unknown>): Promise<T> {
|
||||
if (!response.ok) {
|
||||
throw new Error(response.error?.message ?? 'Task provider request failed')
|
||||
}
|
||||
return (response as RpcSuccess).result as T
|
||||
return response.result as T
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import type { HostTaskItemFileOperations } from './host-task-item-file-operations'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import type { RpcRequestSender } from '../transport/rpc-client'
|
||||
|
||||
export function nativeHostTaskItemFileOperations(client: RpcClient): HostTaskItemFileOperations {
|
||||
export function nativeHostTaskItemFileOperations(
|
||||
client: RpcRequestSender
|
||||
): HostTaskItemFileOperations {
|
||||
return {
|
||||
async refreshChecks(target, headSha) {
|
||||
const checks = await request(client, 'github.prChecks', {
|
||||
@@ -70,14 +72,14 @@ function repoPayload(target: { repoId: string }) {
|
||||
}
|
||||
|
||||
async function request<T = unknown>(
|
||||
client: RpcClient,
|
||||
client: RpcRequestSender,
|
||||
method: string,
|
||||
payload: object,
|
||||
timeoutMs = 30_000
|
||||
): Promise<T> {
|
||||
const response = await client.sendRequest(method, payload, { timeoutMs })
|
||||
if (!response.ok) {
|
||||
throw new Error(response.error.message)
|
||||
throw new Error(response.error?.message ?? 'Task request failed')
|
||||
}
|
||||
return response.result as T
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@ import type {
|
||||
HostTaskItemMutationOperations,
|
||||
HostTaskItemMutationTarget
|
||||
} from './host-task-item-mutation-operations'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import type { RpcRequestSender } from '../transport/rpc-client'
|
||||
|
||||
export function nativeHostTaskItemMutationOperations(
|
||||
client: RpcClient
|
||||
client: RpcRequestSender
|
||||
): HostTaskItemMutationOperations {
|
||||
return {
|
||||
async setClosed(target, closed) {
|
||||
@@ -27,7 +27,7 @@ export function nativeHostTaskItemMutationOperations(
|
||||
}
|
||||
|
||||
function setGitHubClosed(
|
||||
client: RpcClient,
|
||||
client: RpcRequestSender,
|
||||
target: Extract<HostTaskItemMutationTarget, { provider: 'github' }>,
|
||||
closed: boolean
|
||||
) {
|
||||
@@ -46,7 +46,7 @@ function setGitHubClosed(
|
||||
}
|
||||
|
||||
function setGitLabClosed(
|
||||
client: RpcClient,
|
||||
client: RpcRequestSender,
|
||||
target: Extract<HostTaskItemMutationTarget, { provider: 'gitlab' }>,
|
||||
closed: boolean
|
||||
) {
|
||||
@@ -67,7 +67,7 @@ function setGitLabClosed(
|
||||
}
|
||||
|
||||
function updateGitHubMetadata(
|
||||
client: RpcClient,
|
||||
client: RpcRequestSender,
|
||||
target: Extract<HostTaskItemMutationTarget, { provider: 'github' }>,
|
||||
updates: MobileWebTaskItemMetadataUpdates
|
||||
) {
|
||||
@@ -89,7 +89,7 @@ function updateGitHubMetadata(
|
||||
}
|
||||
|
||||
function updateGitLabMetadata(
|
||||
client: RpcClient,
|
||||
client: RpcRequestSender,
|
||||
target: Extract<HostTaskItemMutationTarget, { provider: 'gitlab' }>,
|
||||
updates: MobileWebTaskItemMetadataUpdates
|
||||
) {
|
||||
@@ -122,11 +122,11 @@ function updateGitLabMetadata(
|
||||
}
|
||||
|
||||
function assertMutation(
|
||||
response: Awaited<ReturnType<RpcClient['sendRequest']>>,
|
||||
response: Awaited<ReturnType<RpcRequestSender['sendRequest']>>,
|
||||
fallback: string
|
||||
): void {
|
||||
if (!response.ok) {
|
||||
throw new Error(response.error.message)
|
||||
throw new Error(response.error?.message ?? 'Task request failed')
|
||||
}
|
||||
const result = response.result as { ok?: boolean; error?: string }
|
||||
if (result.ok === false) {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { MobileWebTaskDetailComment } from '../../../src/shared/mobile-web/task-detail-contract'
|
||||
import type { HostTaskItemMutationTarget } from './host-task-item-mutation-operations'
|
||||
import type { HostTaskItemReviewOperations } from './host-task-item-review-operations'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import type { RpcRequestSender } from '../transport/rpc-client'
|
||||
|
||||
export function nativeHostTaskItemReviewOperations(
|
||||
client: RpcClient
|
||||
client: RpcRequestSender
|
||||
): HostTaskItemReviewOperations {
|
||||
return {
|
||||
async addComment(target, body) {
|
||||
@@ -89,7 +89,7 @@ export function nativeHostTaskItemReviewOperations(
|
||||
}
|
||||
|
||||
function addGitLabComment(
|
||||
client: RpcClient,
|
||||
client: RpcRequestSender,
|
||||
target: Extract<HostTaskItemMutationTarget, { provider: 'gitlab' }>,
|
||||
body: string
|
||||
) {
|
||||
@@ -117,11 +117,11 @@ function addGitLabComment(
|
||||
}
|
||||
|
||||
function mutationComment(
|
||||
response: Awaited<ReturnType<RpcClient['sendRequest']>>,
|
||||
response: Awaited<ReturnType<RpcRequestSender['sendRequest']>>,
|
||||
fallback: string
|
||||
): MobileWebTaskDetailComment | undefined {
|
||||
if (!response.ok) {
|
||||
throw new Error(response.error.message)
|
||||
throw new Error(response.error?.message ?? 'Task request failed')
|
||||
}
|
||||
const result = response.result as {
|
||||
ok?: boolean
|
||||
@@ -135,11 +135,11 @@ function mutationComment(
|
||||
}
|
||||
|
||||
function assertMutation(
|
||||
response: Awaited<ReturnType<RpcClient['sendRequest']>>,
|
||||
response: Awaited<ReturnType<RpcRequestSender['sendRequest']>>,
|
||||
fallback: string
|
||||
): void {
|
||||
if (!response.ok) {
|
||||
throw new Error(response.error.message)
|
||||
throw new Error(response.error?.message ?? 'Task request failed')
|
||||
}
|
||||
const result = response.result as { ok?: boolean; error?: string }
|
||||
if (result.ok === false) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { HostTaskLinearOperations } from './host-task-linear-operations'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import type { RpcRequestSender } from '../transport/rpc-client'
|
||||
|
||||
export function nativeHostTaskLinearOperations(client: RpcClient): HostTaskLinearOperations {
|
||||
export function nativeHostTaskLinearOperations(client: RpcRequestSender): HostTaskLinearOperations {
|
||||
return {
|
||||
async connect(apiKey) {
|
||||
assertMutation(
|
||||
@@ -74,13 +74,13 @@ export function nativeHostTaskLinearOperations(client: RpcClient): HostTaskLinea
|
||||
}
|
||||
|
||||
async function request<T = unknown>(
|
||||
client: RpcClient,
|
||||
client: RpcRequestSender,
|
||||
method: string,
|
||||
payload?: object
|
||||
): Promise<T> {
|
||||
const response = await client.sendRequest(method, payload, { timeoutMs: 30_000 })
|
||||
if (!response.ok) {
|
||||
throw new Error(response.error.message)
|
||||
throw new Error(response.error?.message ?? 'Task request failed')
|
||||
}
|
||||
return response.result as T
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { extractLinearIssueReadItems } from './linear-mobile-issue-read'
|
||||
import type { HostTaskListOperations } from './host-task-list-operations'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import type { RpcSuccess } from '../transport/types'
|
||||
import type { RpcRequestSender } from '../transport/rpc-client'
|
||||
|
||||
export function nativeHostTaskListOperations(client: RpcClient): HostTaskListOperations {
|
||||
export function nativeHostTaskListOperations(client: RpcRequestSender): HostTaskListOperations {
|
||||
return {
|
||||
async listGitHub(payload) {
|
||||
return successfulResult(
|
||||
@@ -73,5 +72,5 @@ async function successfulResult<T>(request: Promise<unknown>): Promise<T> {
|
||||
if (!response.ok) {
|
||||
throw new Error(response.error?.message ?? 'Task provider request failed')
|
||||
}
|
||||
return (response as RpcSuccess).result as T
|
||||
return response.result as T
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { persistSetupHookTrustApproval } from './setup-hook-trust'
|
||||
import type { HostTaskPreferenceOperations } from './host-task-preference-operations'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import type { RpcRequestSender } from '../transport/rpc-client'
|
||||
|
||||
export function nativeHostTaskPreferenceOperations(
|
||||
client: RpcClient
|
||||
client: RpcRequestSender
|
||||
): HostTaskPreferenceOperations {
|
||||
return {
|
||||
async updateResume(taskResumeState) {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { HostTaskProjectFileOperations } from './host-task-project-file-operations'
|
||||
import type { HostTaskProjectItemTarget } from './host-task-project-mutation-operations'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import type { RpcRequestSender } from '../transport/rpc-client'
|
||||
|
||||
export function nativeHostTaskProjectFileOperations(
|
||||
client: RpcClient
|
||||
client: RpcRequestSender
|
||||
): HostTaskProjectFileOperations {
|
||||
return {
|
||||
async refreshChecks(target, repoId, headSha) {
|
||||
@@ -60,13 +60,13 @@ function repoPayload(target: HostTaskProjectItemTarget, repoId: string) {
|
||||
}
|
||||
|
||||
async function request<T = unknown>(
|
||||
client: RpcClient,
|
||||
client: RpcRequestSender,
|
||||
method: string,
|
||||
payload: object
|
||||
): Promise<T> {
|
||||
const response = await client.sendRequest(method, payload, { timeoutMs: 30_000 })
|
||||
if (!response.ok) {
|
||||
throw new Error(response.error.message)
|
||||
throw new Error(response.error?.message ?? 'Task request failed')
|
||||
}
|
||||
return response.result as T
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import type {
|
||||
HostTaskProjectItemTarget,
|
||||
HostTaskProjectMutationOperations
|
||||
} from './host-task-project-mutation-operations'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import type { RpcRequestSender } from '../transport/rpc-client'
|
||||
import {
|
||||
fetchAddIssueComment,
|
||||
fetchAddPRReviewCommentReply,
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
const PROJECT_PR_MUTATION_TIMEOUT_MS = 60_000
|
||||
|
||||
export function nativeHostTaskProjectMutationOperations(
|
||||
client: RpcClient
|
||||
client: RpcRequestSender
|
||||
): HostTaskProjectMutationOperations {
|
||||
return {
|
||||
async updateItem(target, updates) {
|
||||
@@ -154,7 +154,7 @@ function slugPayload(target: HostTaskProjectItemTarget) {
|
||||
}
|
||||
|
||||
async function projectMutation<T extends object = object>(
|
||||
client: RpcClient,
|
||||
client: RpcRequestSender,
|
||||
method: string,
|
||||
payload: object
|
||||
): Promise<T> {
|
||||
|
||||
@@ -6,10 +6,10 @@ import type {
|
||||
import type { MobileWebTaskProjectTable } from '../../../src/shared/mobile-web/task-project-table-contract'
|
||||
import type { HostTaskProjectReadOperations } from './host-task-project-read-operations'
|
||||
import { projectGitHubTaskDetail } from './github-task-detail-projection'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import type { RpcRequestSender } from '../transport/rpc-client'
|
||||
|
||||
export function nativeHostTaskProjectReadOperations(
|
||||
client: RpcClient
|
||||
client: RpcRequestSender
|
||||
): HostTaskProjectReadOperations {
|
||||
return {
|
||||
async listAccessible(host) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { HostTaskProviderWriteOperations } from './host-task-provider-write-operations'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import type { RpcRequestSender } from '../transport/rpc-client'
|
||||
|
||||
export function nativeHostTaskProviderWriteOperations(
|
||||
client: RpcClient
|
||||
client: RpcRequestSender
|
||||
): HostTaskProviderWriteOperations {
|
||||
return {
|
||||
async createIssue(payload) {
|
||||
@@ -15,7 +15,7 @@ export function nativeHostTaskProviderWriteOperations(
|
||||
}
|
||||
)
|
||||
if (!response.ok) {
|
||||
throw new Error(response.error.message)
|
||||
throw new Error(response.error?.message ?? 'Task request failed')
|
||||
}
|
||||
const result = response.result as {
|
||||
ok?: boolean
|
||||
@@ -41,7 +41,7 @@ export function nativeHostTaskProviderWriteOperations(
|
||||
{ timeoutMs: 15_000 }
|
||||
)
|
||||
if (!response.ok) {
|
||||
throw new Error(response.error.message)
|
||||
throw new Error(response.error?.message ?? 'Task request failed')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,10 +6,9 @@ import type {
|
||||
HostTaskReadOperations,
|
||||
HostTaskRepository
|
||||
} from './host-task-read-operations'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import type { RpcSuccess } from '../transport/types'
|
||||
import type { RpcRequestReply, RpcRequestSender } from '../transport/rpc-client'
|
||||
|
||||
export function nativeHostTaskReadOperations(client: RpcClient): HostTaskReadOperations {
|
||||
export function nativeHostTaskReadOperations(client: RpcRequestSender): HostTaskReadOperations {
|
||||
return {
|
||||
bootstrap: () => bootstrap(client),
|
||||
listRepositories: () => listRepositories(client),
|
||||
@@ -18,10 +17,10 @@ export function nativeHostTaskReadOperations(client: RpcClient): HostTaskReadOpe
|
||||
}
|
||||
}
|
||||
|
||||
async function bootstrap(client: RpcClient): Promise<HostTaskBootstrap> {
|
||||
async function bootstrap(client: RpcRequestSender): Promise<HostTaskBootstrap> {
|
||||
const statusResponse = await client.sendRequest('status.get')
|
||||
requireSuccess(statusResponse)
|
||||
const status = (statusResponse as RpcSuccess).result as { capabilities?: string[] }
|
||||
const status = statusResponse.result as { capabilities?: string[] }
|
||||
if (!status.capabilities?.includes(MOBILE_TASKS_CAPABILITY)) {
|
||||
return emptyBootstrap(false)
|
||||
}
|
||||
@@ -53,17 +52,17 @@ async function bootstrap(client: RpcClient): Promise<HostTaskBootstrap> {
|
||||
}
|
||||
}
|
||||
|
||||
async function listRepositories(client: RpcClient): Promise<HostTaskRepository[]> {
|
||||
async function listRepositories(client: RpcRequestSender): Promise<HostTaskRepository[]> {
|
||||
const response = await client.sendRequest('repo.list')
|
||||
requireSuccess(response)
|
||||
const result = (response as RpcSuccess).result as { repos?: HostTaskRepository[] }
|
||||
const result = response.result as { repos?: HostTaskRepository[] }
|
||||
return result.repos ?? []
|
||||
}
|
||||
|
||||
async function loadLinearContext(client: RpcClient): Promise<HostTaskLinearContext> {
|
||||
async function loadLinearContext(client: RpcRequestSender): Promise<HostTaskLinearContext> {
|
||||
const statusResponse = await client.sendRequest('linear.status')
|
||||
requireSuccess(statusResponse)
|
||||
const status = normalizeLinearStatus((statusResponse as RpcSuccess).result)
|
||||
const status = normalizeLinearStatus(statusResponse.result)
|
||||
if (!status.connected) {
|
||||
return { status, teams: [] }
|
||||
}
|
||||
@@ -75,12 +74,12 @@ async function loadLinearContext(client: RpcClient): Promise<HostTaskLinearConte
|
||||
requireSuccess(teamsResponse)
|
||||
return {
|
||||
status: { ...status, selectedWorkspaceId: workspaceId },
|
||||
teams: (teamsResponse as RpcSuccess).result as HostTaskLinearContext['teams']
|
||||
teams: teamsResponse.result as HostTaskLinearContext['teams']
|
||||
}
|
||||
}
|
||||
|
||||
async function resolveGitHubRepoSlug(
|
||||
client: RpcClient,
|
||||
client: RpcRequestSender,
|
||||
repoId: string
|
||||
): Promise<GitHubOwnerRepo | null> {
|
||||
const response = await client.sendRequest(
|
||||
@@ -89,7 +88,7 @@ async function resolveGitHubRepoSlug(
|
||||
{ timeoutMs: 30_000 }
|
||||
)
|
||||
requireSuccess(response)
|
||||
return (response as RpcSuccess).result as GitHubOwnerRepo | null
|
||||
return response.result as GitHubOwnerRepo | null
|
||||
}
|
||||
|
||||
function emptyBootstrap(supported: boolean): HostTaskBootstrap {
|
||||
@@ -113,11 +112,13 @@ function normalizeLinearStatus(value: unknown): HostTaskBootstrap['linearStatus'
|
||||
}
|
||||
}
|
||||
|
||||
function successResult<T>(response: { ok: boolean; result?: unknown }): T | null {
|
||||
function successResult<T>(response: RpcRequestReply): T | null {
|
||||
return response.ok ? (response.result as T) : null
|
||||
}
|
||||
|
||||
function requireSuccess(response: { ok: boolean; error?: { message?: string } }): void {
|
||||
function requireSuccess(
|
||||
response: RpcRequestReply
|
||||
): asserts response is { ok: true; result: unknown } {
|
||||
if (!response.ok) {
|
||||
throw new Error(response.error?.message ?? 'Task operation failed')
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { PersistedTrustedOrcaHooks } from '../../../src/shared/orca-yaml-hook-types'
|
||||
import type { RpcClient } from '../transport/rpc-client'
|
||||
import type { RpcRequestSender } from '../transport/rpc-client'
|
||||
|
||||
export type SetupHookTrust = {
|
||||
contentHash: string
|
||||
@@ -38,7 +38,7 @@ export function trustedOrcaHooksWithSetupApproval(args: {
|
||||
}
|
||||
|
||||
export async function persistSetupHookTrustApproval(args: {
|
||||
client: RpcClient
|
||||
client: RpcRequestSender
|
||||
trust: PersistedTrustedOrcaHooks
|
||||
repoId: string
|
||||
contentHash: string
|
||||
@@ -47,7 +47,7 @@ export async function persistSetupHookTrustApproval(args: {
|
||||
const next = trustedOrcaHooksWithSetupApproval(args)
|
||||
const response = await args.client.sendRequest('ui.set', { trustedOrcaHooks: next })
|
||||
if (!response.ok) {
|
||||
throw new Error(response.error.message)
|
||||
throw new Error(response.error?.message ?? 'Hook trust update failed')
|
||||
}
|
||||
return next
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import type { MobileWebHostRequestClient } from '../../../src/mobile-web/src/mobile-web-host-request-client'
|
||||
import type { RpcRequestSender } from '../transport/rpc-client'
|
||||
|
||||
/** The hosted page reaches the same desktop methods the native app calls, so the task operation
|
||||
* modules are shared verbatim. A desktop error arrives as a rejected bridge request, never as an
|
||||
* `ok: false` reply, which is why this sender only ever resolves successes. */
|
||||
export function webHostTaskRpcSender(host: MobileWebHostRequestClient): RpcRequestSender {
|
||||
return {
|
||||
async sendRequest(method, params) {
|
||||
return { ok: true, result: await host.request({ method, params: hostParams(params) }) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function hostParams(params: unknown): Record<string, unknown> {
|
||||
return typeof params === 'object' && params !== null && !Array.isArray(params)
|
||||
? (params as Record<string, unknown>)
|
||||
: {}
|
||||
}
|
||||
@@ -18,6 +18,22 @@ export type SendRequestOptions = {
|
||||
beforeSend?: () => void
|
||||
}
|
||||
|
||||
/** A unary reply as its caller reads it. The hosted page answers over the capability bridge, which
|
||||
* carries no request id or runtime metadata, so those stay on the transport's own `RpcResponse`. */
|
||||
export type RpcRequestReply =
|
||||
| { ok: true; result: unknown }
|
||||
| { ok: false; error?: { code?: string; message?: string } }
|
||||
|
||||
/** The only surface a unary caller needs, so the same operation modules serve the native app over a
|
||||
* socket and the hosted page over the bridge. */
|
||||
export type RpcRequestSender = {
|
||||
sendRequest: (
|
||||
method: string,
|
||||
params?: unknown,
|
||||
options?: SendRequestOptions
|
||||
) => Promise<RpcRequestReply>
|
||||
}
|
||||
|
||||
type StreamingListener = (result: unknown) => void
|
||||
|
||||
export type RpcClient = {
|
||||
|
||||
Reference in New Issue
Block a user