@octane-xplat/mcp 0.1.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +342 -1
- package/dist/_virtual/_rolldown/runtime.js +27 -0
- package/dist/actions/bridge.js +1 -0
- package/dist/context.js +0 -0
- package/dist/contracts/actions.js +1 -0
- package/dist/contracts/boundaries.js +1 -0
- package/dist/contracts/capabilities.js +1 -0
- package/dist/contracts/code.js +1 -0
- package/dist/contracts/errors.js +8 -0
- package/dist/contracts/evidence.js +8 -0
- package/dist/contracts/identity.js +8 -0
- package/dist/contracts/index.js +1 -0
- package/dist/contracts/inspection.js +1 -0
- package/dist/contracts/policy.js +8 -0
- package/dist/contracts/sessions.js +1 -0
- package/dist/contracts/status.js +1 -0
- package/dist/discovery.js +8 -0
- package/dist/docs.js +63 -0
- package/dist/inspection/service.js +3 -0
- package/dist/inspection/tools.js +43 -0
- package/dist/runtime/logs.js +1 -0
- package/dist/runtime/process.js +1 -0
- package/dist/runtime/service.js +1 -0
- package/dist/runtime/targets.js +1 -0
- package/dist/runtime/tools.js +119 -0
- package/dist/server.js +324 -0
- package/dist/status.js +8 -0
- package/dist/xplat-mcp.js +68 -0
- package/package.json +43 -4
- package/src/actions/bridge.ts +2 -0
- package/src/context.ts +108 -0
- package/src/contracts/actions.ts +3 -0
- package/src/contracts/boundaries.ts +3 -0
- package/src/contracts/capabilities.ts +3 -0
- package/src/contracts/code.ts +3 -0
- package/src/contracts/errors.ts +3 -0
- package/src/contracts/evidence.ts +3 -0
- package/src/contracts/identity.ts +3 -0
- package/src/contracts/index.ts +3 -0
- package/src/contracts/inspection.ts +3 -0
- package/src/contracts/policy.ts +3 -0
- package/src/contracts/sessions.ts +3 -0
- package/src/contracts/status.ts +3 -0
- package/src/discovery.ts +2 -0
- package/src/docs.ts +89 -0
- package/src/inspection/service.ts +5 -0
- package/src/inspection/tools.ts +61 -0
- package/src/runtime/logs.ts +2 -0
- package/src/runtime/process.ts +2 -0
- package/src/runtime/service.ts +2 -0
- package/src/runtime/targets.ts +2 -0
- package/src/runtime/tools.ts +136 -0
- package/src/server.ts +580 -0
- package/src/status.ts +2 -0
- package/src/xplat-mcp.ts +96 -0
- package/types/contracts/actions.d.ts +209 -0
- package/types/contracts/boundaries.d.ts +6 -0
- package/types/contracts/capabilities.d.ts +106 -0
- package/types/contracts/code.d.ts +134 -0
- package/types/contracts/errors.d.ts +75 -0
- package/types/contracts/evidence.d.ts +115 -0
- package/types/contracts/identity.d.ts +127 -0
- package/types/contracts/index.d.ts +11 -0
- package/types/contracts/inspection.d.ts +45 -0
- package/types/contracts/policy.d.ts +86 -0
- package/types/contracts/sessions.d.ts +160 -0
- package/types/contracts/status.d.ts +40 -0
package/src/context.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// Shared internal types for the MCP transport adapter. The context object is
|
|
2
|
+
// assembled by createWorkspaceContext in the shared service layer
|
|
3
|
+
// (@octane-xplat/cli/mcp/status); its registries/stores are typed by the
|
|
4
|
+
// published contracts while service slots (runtime, docs, inspection,
|
|
5
|
+
// dev-actions) keep the adapter-facing surface the tools actually use.
|
|
6
|
+
|
|
7
|
+
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'
|
|
8
|
+
import type { discoverWorkspace } from '@octane-xplat/cli/mcp/discovery'
|
|
9
|
+
import type { CapabilityRegistry } from '../types/contracts/capabilities'
|
|
10
|
+
import type { CodeProvider } from '../types/contracts/code'
|
|
11
|
+
import type { ToolOutcome } from '../types/contracts/errors'
|
|
12
|
+
import type { EvidenceStore } from '../types/contracts/evidence'
|
|
13
|
+
import type { AppRef, Platform } from '../types/contracts/identity'
|
|
14
|
+
import type { PolicyStore } from '../types/contracts/policy'
|
|
15
|
+
import type { Job, JobRegistry, SessionRegistry } from '../types/contracts/sessions'
|
|
16
|
+
import type { ServerInfo } from '../types/contracts/status'
|
|
17
|
+
|
|
18
|
+
export type Discovery = ReturnType<typeof discoverWorkspace>
|
|
19
|
+
|
|
20
|
+
/** The app-binding result: explicit/configured binding, sole app, or a list the developer must choose from. */
|
|
21
|
+
export type Selection =
|
|
22
|
+
| { state: 'bound'; app: AppRef; reason: string | null }
|
|
23
|
+
| { state: 'ambiguous'; candidates: AppRef[] }
|
|
24
|
+
| { state: 'none'; reason: string }
|
|
25
|
+
|
|
26
|
+
export type SelectionReason = 'explicit' | 'configured' | 'sole-app' | null
|
|
27
|
+
|
|
28
|
+
export interface RuntimeService {
|
|
29
|
+
registerCapabilities(): void
|
|
30
|
+
listTargets(args: { platform?: Platform }): Promise<any>
|
|
31
|
+
build(args: { platform: Platform; requestKey?: string }): Promise<any>
|
|
32
|
+
launch(args: {
|
|
33
|
+
platform: Platform
|
|
34
|
+
targetId?: string
|
|
35
|
+
requestKey?: string
|
|
36
|
+
browserMode?: 'headless' | 'headed'
|
|
37
|
+
}): Promise<any>
|
|
38
|
+
attach(args: { platform: Platform; targetId?: string }): Promise<any>
|
|
39
|
+
readLogs(args: {
|
|
40
|
+
sessionId?: string
|
|
41
|
+
jobId?: string
|
|
42
|
+
cursor?: number
|
|
43
|
+
limit?: number
|
|
44
|
+
stream?: 'stdout' | 'stderr' | 'lifecycle'
|
|
45
|
+
}): any
|
|
46
|
+
endSession(sessionId: string): Promise<any>
|
|
47
|
+
endAll(): Promise<any>
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface DocsProvider {
|
|
51
|
+
status(app: AppRef | null): unknown
|
|
52
|
+
serviceFor(app: AppRef): {
|
|
53
|
+
search(args: any): Promise<any>
|
|
54
|
+
read(args: any): Promise<any>
|
|
55
|
+
guidance(args: any): Promise<any>
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface InspectionService {
|
|
60
|
+
inspect(args: any): Promise<any>
|
|
61
|
+
interact(args: any): Promise<any>
|
|
62
|
+
screenshot(args: any): Promise<any>
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface DevActionsService {
|
|
66
|
+
list(app: AppRef): Promise<any>
|
|
67
|
+
invoke(
|
|
68
|
+
app: AppRef,
|
|
69
|
+
args: {
|
|
70
|
+
appInstanceId?: string
|
|
71
|
+
actionId: string
|
|
72
|
+
input: Record<string, unknown>
|
|
73
|
+
registryRevision?: number
|
|
74
|
+
requestKey?: string
|
|
75
|
+
},
|
|
76
|
+
): Promise<any>
|
|
77
|
+
describeJob(jobId: string): { job: Job; [key: string]: unknown } | null
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface WorkspaceContext {
|
|
81
|
+
workspaceRoot: string
|
|
82
|
+
configuredAppSelector: string | null
|
|
83
|
+
serverIdentity: ServerInfo
|
|
84
|
+
discovery: Discovery
|
|
85
|
+
boundApp: AppRef | null
|
|
86
|
+
selectionReason: SelectionReason
|
|
87
|
+
capabilities: CapabilityRegistry
|
|
88
|
+
policy: PolicyStore
|
|
89
|
+
sessions: SessionRegistry
|
|
90
|
+
jobs: JobRegistry
|
|
91
|
+
evidence: EvidenceStore
|
|
92
|
+
docsStatus: (app: AppRef | null) => unknown
|
|
93
|
+
codeIndexStatus: (app: AppRef | null) => unknown
|
|
94
|
+
code: CodeProvider | null
|
|
95
|
+
docs: DocsProvider
|
|
96
|
+
devActions: DevActionsService
|
|
97
|
+
runtime: RuntimeService
|
|
98
|
+
inspection: InspectionService
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** The structuredContent envelope every tool result shares. */
|
|
102
|
+
export interface ToolResponse extends CallToolResult {
|
|
103
|
+
structuredContent: ToolOutcome<unknown>
|
|
104
|
+
isError: boolean
|
|
105
|
+
content: [{ type: 'text'; text: string }]
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export type Respond = (outcome: ToolOutcome<unknown>) => ToolResponse
|
package/src/discovery.ts
ADDED
package/src/docs.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// Docs tool registration (MCP adapter). The provider itself lives in the
|
|
2
|
+
// shared service layer at @octane-xplat/cli/mcp/docs-provider — the CLI's
|
|
3
|
+
// `docs`/`guidance` commands project the same service.
|
|
4
|
+
import { z } from 'zod'
|
|
5
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
6
|
+
import { contractError, toContractError } from './contracts/errors.ts'
|
|
7
|
+
import { PLATFORMS } from './contracts/identity.ts'
|
|
8
|
+
import { resolveSelection } from './status.ts'
|
|
9
|
+
import type { Respond, Selection, WorkspaceContext } from './context.ts'
|
|
10
|
+
import type { Platform } from '../types/contracts/identity'
|
|
11
|
+
|
|
12
|
+
export function registerDocsTools(server: McpServer, context: WorkspaceContext, respond: Respond) {
|
|
13
|
+
const run =
|
|
14
|
+
(method: 'search' | 'read' | 'guidance') =>
|
|
15
|
+
async (args: any): Promise<ReturnType<Respond>> => {
|
|
16
|
+
const permission = context.policy.require('workspace')
|
|
17
|
+
if (!permission.granted) {
|
|
18
|
+
return respond({ ok: false, error: permission.error })
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const selection = resolveSelection(context) as Selection
|
|
22
|
+
if (selection.state !== 'bound') {
|
|
23
|
+
return respond({
|
|
24
|
+
ok: false,
|
|
25
|
+
error: contractError(
|
|
26
|
+
selection.state === 'ambiguous' ? 'ambiguous_selection' : 'invalid_selection',
|
|
27
|
+
'Bind an app with xplat_select_app before retrieving docs.',
|
|
28
|
+
),
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const data = await context.docs.serviceFor(selection.app)[method](args)
|
|
34
|
+
return respond({ ok: true, data })
|
|
35
|
+
} catch (error) {
|
|
36
|
+
return respond({ ok: false, error: toContractError(error) })
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
server.registerTool(
|
|
41
|
+
'xplat_docs_search',
|
|
42
|
+
{
|
|
43
|
+
description:
|
|
44
|
+
'Search the selected app’s installed-version docs. Results include version/snapshot provenance, unavailable packages, conflicts, and separately labeled project guidance.',
|
|
45
|
+
inputSchema: {
|
|
46
|
+
query: z.string().trim().min(1).max(500),
|
|
47
|
+
package: z.string().optional(),
|
|
48
|
+
version: z
|
|
49
|
+
.string()
|
|
50
|
+
.optional()
|
|
51
|
+
.describe('Explicit comparison version, visibly outside the installed binding.'),
|
|
52
|
+
platform: z.enum(PLATFORMS as Platform[]).optional(),
|
|
53
|
+
corpus: z
|
|
54
|
+
.enum(['guide', 'recipe', 'design-note', 'implementation-note'])
|
|
55
|
+
.optional()
|
|
56
|
+
.default('guide'),
|
|
57
|
+
limit: z.number().int().min(1).max(50).optional(),
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
run('search'),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
server.registerTool(
|
|
64
|
+
'xplat_docs_read',
|
|
65
|
+
{
|
|
66
|
+
description:
|
|
67
|
+
'Read a bounded documentation page from a search ref, or package and path, preserving its version binding.',
|
|
68
|
+
inputSchema: {
|
|
69
|
+
ref: z.string().optional(),
|
|
70
|
+
package: z.string().optional(),
|
|
71
|
+
path: z.string().optional(),
|
|
72
|
+
version: z.string().optional(),
|
|
73
|
+
offset: z.number().int().min(0).optional(),
|
|
74
|
+
limit: z.number().int().min(1).max(20000).optional(),
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
run('read'),
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
server.registerTool(
|
|
81
|
+
'xplat_guidance',
|
|
82
|
+
{
|
|
83
|
+
description:
|
|
84
|
+
'Retrieve tailored guidance for the selected app’s actual installed package versions.',
|
|
85
|
+
inputSchema: {},
|
|
86
|
+
},
|
|
87
|
+
run('guidance'),
|
|
88
|
+
)
|
|
89
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// The inspection service moved to @octane-xplat/cli (shared agent service
|
|
2
|
+
// layer); the MCP tool registration lives in ./tools.mjs. Both names are
|
|
3
|
+
// re-exported here so existing importers keep working.
|
|
4
|
+
export { createInspectionService } from '@octane-xplat/cli/mcp/inspection/service'
|
|
5
|
+
export { registerInspectionTools } from './tools.ts'
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Inspection tool registration (MCP adapter). The service lives in the
|
|
2
|
+
// shared layer at @octane-xplat/cli/mcp/inspection/service; this file only
|
|
3
|
+
// binds it to MCP tools.
|
|
4
|
+
import { z } from 'zod'
|
|
5
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
6
|
+
import type { Respond, WorkspaceContext } from '../context.ts'
|
|
7
|
+
|
|
8
|
+
export function registerInspectionTools(
|
|
9
|
+
server: McpServer,
|
|
10
|
+
context: WorkspaceContext,
|
|
11
|
+
respond: Respond,
|
|
12
|
+
) {
|
|
13
|
+
const control = z.object({ kind: z.enum(['id', 'testID']), name: z.string().min(1).max(200) })
|
|
14
|
+
const run =
|
|
15
|
+
(method: 'inspect' | 'interact' | 'screenshot') =>
|
|
16
|
+
async (args: any): Promise<ReturnType<Respond>> => {
|
|
17
|
+
const data = await context.inspection[method](args)
|
|
18
|
+
return data.error ? respond({ ok: false, error: data.error }) : respond({ ok: true, data })
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
server.registerTool(
|
|
22
|
+
'xplat_inspect',
|
|
23
|
+
{
|
|
24
|
+
description:
|
|
25
|
+
'Bounded main-document named DOM hosts in the owned Chromium session. Explicit id/testID selector; duplicates fail. Password/private values are redacted. Includes document/session/build identity and coverage.',
|
|
26
|
+
inputSchema: {
|
|
27
|
+
sessionId: z.string(),
|
|
28
|
+
control: control.optional(),
|
|
29
|
+
offset: z.number().int().min(0).optional(),
|
|
30
|
+
limit: z.number().int().min(1).max(100).optional(),
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
run('inspect'),
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
server.registerTool(
|
|
37
|
+
'xplat_interact',
|
|
38
|
+
{
|
|
39
|
+
description:
|
|
40
|
+
'Send scoped input to one stable control from an inspected document. dom-event sends a CDP mouse click; os-input sends Space/Enter via the macOS event system to owned headed Chromium. Reserve desktop input for OS actions. Input delivery is not an app assertion.',
|
|
41
|
+
inputSchema: {
|
|
42
|
+
sessionId: z.string(),
|
|
43
|
+
documentId: z.string(),
|
|
44
|
+
control,
|
|
45
|
+
mechanism: z.enum(['dom-event', 'os-input']),
|
|
46
|
+
key: z.enum(['Space', 'Enter']).optional().default('Space'),
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
run('interact'),
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
server.registerTool(
|
|
53
|
+
'xplat_screenshot',
|
|
54
|
+
{
|
|
55
|
+
description:
|
|
56
|
+
'Capture the owned Chromium viewport only with a current visual-capture grant. Returns a session-scoped artifact and provenance; capability discovery never grants approval.',
|
|
57
|
+
inputSchema: { sessionId: z.string() },
|
|
58
|
+
},
|
|
59
|
+
run('screenshot'),
|
|
60
|
+
)
|
|
61
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// Runtime MCP tools (work item D). Thin bindings over the runtime service —
|
|
2
|
+
// all session/job/ownership behavior lives in service.mjs and the shared
|
|
3
|
+
// contracts.
|
|
4
|
+
|
|
5
|
+
import { z } from 'zod'
|
|
6
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
7
|
+
import { PLATFORMS } from '../contracts/identity.ts'
|
|
8
|
+
import type { Respond, WorkspaceContext } from '../context.ts'
|
|
9
|
+
import type { Platform } from '../../types/contracts/identity'
|
|
10
|
+
|
|
11
|
+
export function registerRuntimeTools(
|
|
12
|
+
server: McpServer,
|
|
13
|
+
context: WorkspaceContext,
|
|
14
|
+
respond: Respond,
|
|
15
|
+
) {
|
|
16
|
+
const runtime = context.runtime
|
|
17
|
+
|
|
18
|
+
server.registerTool(
|
|
19
|
+
'xplat_targets',
|
|
20
|
+
{
|
|
21
|
+
description:
|
|
22
|
+
'Discover launchable targets for the bound app: simulators, emulators, devices and host targets with availability, missing-toolchain reasons, and session busy/ownership state.',
|
|
23
|
+
inputSchema: {
|
|
24
|
+
platform: z
|
|
25
|
+
.enum(PLATFORMS as Platform[])
|
|
26
|
+
.optional()
|
|
27
|
+
.describe('Limit to one platform'),
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
async ({ platform }) => {
|
|
31
|
+
const result = await runtime.listTargets({ platform })
|
|
32
|
+
return result.error
|
|
33
|
+
? respond({ ok: false, error: result.error })
|
|
34
|
+
: respond({ ok: true, data: result })
|
|
35
|
+
},
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
server.registerTool(
|
|
39
|
+
'xplat_build',
|
|
40
|
+
{
|
|
41
|
+
description:
|
|
42
|
+
'Build the bound app for one platform as a background job (the same commands `xplat build` runs). Returns a job id — poll xplat_job_status and read output with xplat_logs. Repeating with the same requestKey returns the live job instead of duplicating it.',
|
|
43
|
+
inputSchema: {
|
|
44
|
+
platform: z.enum(PLATFORMS as Platform[]),
|
|
45
|
+
requestKey: z.string().optional().describe('Idempotence key for retries'),
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
async ({ platform, requestKey }) => {
|
|
49
|
+
const result = await runtime.build({ platform, requestKey })
|
|
50
|
+
return result.error
|
|
51
|
+
? respond({ ok: false, error: result.error })
|
|
52
|
+
: respond({ ok: true, data: result })
|
|
53
|
+
},
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
server.registerTool(
|
|
57
|
+
'xplat_launch',
|
|
58
|
+
{
|
|
59
|
+
description:
|
|
60
|
+
'Start an owned development session: bind one app to one target and run the platform dev command as a job. The job completes when readiness is confirmed — a failed launch after a successful build reports accurately, and the session keeps the app alive until xplat_session_end.',
|
|
61
|
+
inputSchema: {
|
|
62
|
+
platform: z.enum(PLATFORMS as Platform[]),
|
|
63
|
+
targetId: z
|
|
64
|
+
.string()
|
|
65
|
+
.optional()
|
|
66
|
+
.describe('Target from xplat_targets; required when several are available'),
|
|
67
|
+
requestKey: z.string().optional().describe('Idempotence key for retries'),
|
|
68
|
+
browserMode: z
|
|
69
|
+
.enum(['headless', 'headed'])
|
|
70
|
+
.optional()
|
|
71
|
+
.describe(
|
|
72
|
+
'Web only. Headed mode enables scoped OS keyboard input on qualified hosts; reserve desktop input before using it.',
|
|
73
|
+
),
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
async ({ platform, targetId, requestKey, browserMode }) => {
|
|
77
|
+
const result = await runtime.launch({ platform, targetId, requestKey, browserMode })
|
|
78
|
+
return result.error
|
|
79
|
+
? respond({ ok: false, error: result.error })
|
|
80
|
+
: respond({ ok: true, data: result })
|
|
81
|
+
},
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
server.registerTool(
|
|
85
|
+
'xplat_attach',
|
|
86
|
+
{
|
|
87
|
+
description:
|
|
88
|
+
'Attach to a compatible development session that is already running — never restarts or terminates it. Reports candidates per platform; attach binds a preserved user-owned session.',
|
|
89
|
+
inputSchema: {
|
|
90
|
+
platform: z.enum(PLATFORMS as Platform[]),
|
|
91
|
+
targetId: z
|
|
92
|
+
.string()
|
|
93
|
+
.optional()
|
|
94
|
+
.describe('Candidate target; required when several are attachable'),
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
async ({ platform, targetId }) => {
|
|
98
|
+
const result = await runtime.attach({ platform, targetId })
|
|
99
|
+
return result.error
|
|
100
|
+
? respond({ ok: false, error: result.error })
|
|
101
|
+
: respond({ ok: true, data: result })
|
|
102
|
+
},
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
server.registerTool(
|
|
106
|
+
'xplat_logs',
|
|
107
|
+
{
|
|
108
|
+
description:
|
|
109
|
+
'Bounded, ordered log read for a session or build job. Cursor-paginated: pass the previous nextCursor to continue; truncated/dropped flags reveal gaps and reset marks a restarted capture. A stopped or disconnected session reports its capture as historical.',
|
|
110
|
+
inputSchema: {
|
|
111
|
+
sessionId: z.string().optional(),
|
|
112
|
+
jobId: z.string().optional(),
|
|
113
|
+
cursor: z.number().optional().describe('Continue after this record seq'),
|
|
114
|
+
limit: z.number().int().positive().max(5000).optional(),
|
|
115
|
+
stream: z.enum(['stdout', 'stderr', 'lifecycle']).optional(),
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
async ({ sessionId, jobId, cursor, limit, stream }) => {
|
|
119
|
+
if (!sessionId && !jobId) {
|
|
120
|
+
return respond({
|
|
121
|
+
ok: false,
|
|
122
|
+
error: {
|
|
123
|
+
code: 'invalid_selection',
|
|
124
|
+
message: 'Pass a sessionId or a jobId.',
|
|
125
|
+
recovery: { action: 'Use xplat_sessions or a build/launch job id.' },
|
|
126
|
+
},
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const result = runtime.readLogs({ sessionId, jobId, cursor, limit, stream })
|
|
131
|
+
return result.error
|
|
132
|
+
? respond({ ok: false, error: result.error })
|
|
133
|
+
: respond({ ok: true, data: result })
|
|
134
|
+
},
|
|
135
|
+
)
|
|
136
|
+
}
|