@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.
Files changed (68) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +342 -1
  3. package/dist/_virtual/_rolldown/runtime.js +27 -0
  4. package/dist/actions/bridge.js +1 -0
  5. package/dist/context.js +0 -0
  6. package/dist/contracts/actions.js +1 -0
  7. package/dist/contracts/boundaries.js +1 -0
  8. package/dist/contracts/capabilities.js +1 -0
  9. package/dist/contracts/code.js +1 -0
  10. package/dist/contracts/errors.js +8 -0
  11. package/dist/contracts/evidence.js +8 -0
  12. package/dist/contracts/identity.js +8 -0
  13. package/dist/contracts/index.js +1 -0
  14. package/dist/contracts/inspection.js +1 -0
  15. package/dist/contracts/policy.js +8 -0
  16. package/dist/contracts/sessions.js +1 -0
  17. package/dist/contracts/status.js +1 -0
  18. package/dist/discovery.js +8 -0
  19. package/dist/docs.js +63 -0
  20. package/dist/inspection/service.js +3 -0
  21. package/dist/inspection/tools.js +43 -0
  22. package/dist/runtime/logs.js +1 -0
  23. package/dist/runtime/process.js +1 -0
  24. package/dist/runtime/service.js +1 -0
  25. package/dist/runtime/targets.js +1 -0
  26. package/dist/runtime/tools.js +119 -0
  27. package/dist/server.js +324 -0
  28. package/dist/status.js +8 -0
  29. package/dist/xplat-mcp.js +68 -0
  30. package/package.json +43 -4
  31. package/src/actions/bridge.ts +2 -0
  32. package/src/context.ts +108 -0
  33. package/src/contracts/actions.ts +3 -0
  34. package/src/contracts/boundaries.ts +3 -0
  35. package/src/contracts/capabilities.ts +3 -0
  36. package/src/contracts/code.ts +3 -0
  37. package/src/contracts/errors.ts +3 -0
  38. package/src/contracts/evidence.ts +3 -0
  39. package/src/contracts/identity.ts +3 -0
  40. package/src/contracts/index.ts +3 -0
  41. package/src/contracts/inspection.ts +3 -0
  42. package/src/contracts/policy.ts +3 -0
  43. package/src/contracts/sessions.ts +3 -0
  44. package/src/contracts/status.ts +3 -0
  45. package/src/discovery.ts +2 -0
  46. package/src/docs.ts +89 -0
  47. package/src/inspection/service.ts +5 -0
  48. package/src/inspection/tools.ts +61 -0
  49. package/src/runtime/logs.ts +2 -0
  50. package/src/runtime/process.ts +2 -0
  51. package/src/runtime/service.ts +2 -0
  52. package/src/runtime/targets.ts +2 -0
  53. package/src/runtime/tools.ts +136 -0
  54. package/src/server.ts +580 -0
  55. package/src/status.ts +2 -0
  56. package/src/xplat-mcp.ts +96 -0
  57. package/types/contracts/actions.d.ts +209 -0
  58. package/types/contracts/boundaries.d.ts +6 -0
  59. package/types/contracts/capabilities.d.ts +106 -0
  60. package/types/contracts/code.d.ts +134 -0
  61. package/types/contracts/errors.d.ts +75 -0
  62. package/types/contracts/evidence.d.ts +115 -0
  63. package/types/contracts/identity.d.ts +127 -0
  64. package/types/contracts/index.d.ts +11 -0
  65. package/types/contracts/inspection.d.ts +45 -0
  66. package/types/contracts/policy.d.ts +86 -0
  67. package/types/contracts/sessions.d.ts +160 -0
  68. package/types/contracts/status.d.ts +40 -0
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Project identity contract — what the server detects and reports about the
3
+ * developer's workspace during setup, and the shape of the project-status
4
+ * report every later package (docs, code, runtime) plugs into.
5
+ */
6
+
7
+ export const PLATFORMS: readonly Platform[]
8
+
9
+ /** The six release-boundary targets. `web` means Chromium acceptance checks. */
10
+ export type Platform = 'web' | 'ios' | 'android' | 'macos' | 'linux' | 'windows'
11
+
12
+ /** A detected application inside the selected workspace. */
13
+ export interface AppRef {
14
+ /** Stable identity for selection and binding: workspace-relative root path ('.' for the root app). */
15
+ id: string
16
+ /** package.json name, falling back to the directory basename. */
17
+ name: string
18
+ /** Absolute path. */
19
+ root: string
20
+ /** Path relative to the workspace root. */
21
+ relativeRoot: string
22
+ /**
23
+ * `consumer-app` — a deployable Octane Xplat application (the primary setup path).
24
+ * `framework-package` — a package inside the octane-xplat framework repository
25
+ * (identified separately, never silently treated as the consumer app).
26
+ */
27
+ kind: 'consumer-app' | 'framework-package'
28
+ /** Platforms this app declares (from its config and dependencies). */
29
+ platforms: Platform[]
30
+ }
31
+
32
+ /** Where a package version came from. `installed` is the only version that binds docs. */
33
+ export type VersionSource = 'installed' | 'workspace' | 'lockfile' | 'unresolved'
34
+
35
+ export interface PackageVersion {
36
+ /** e.g. `@octane-xplat/ui`. */
37
+ name: string
38
+ version: string | null
39
+ source: VersionSource
40
+ /** The declared range in the app's manifest, when it differs from the resolved version. */
41
+ declared?: string
42
+ }
43
+
44
+ export interface FrameworkBinding {
45
+ packages: PackageVersion[]
46
+ /**
47
+ * `installed` — resolved from the app's actual node_modules (binds documentation).
48
+ * `provisional` — lockfile/declared only; the real install could not be verified.
49
+ * `unresolved` — no version information at all.
50
+ */
51
+ status: 'installed' | 'provisional' | 'unresolved'
52
+ /** Set when the lockfile and the actual installation disagree; installation wins. */
53
+ discrepancy?: string
54
+ }
55
+
56
+ export interface GuidanceSource {
57
+ kind: 'agents-md' | 'claude-md' | 'devin' | 'framework' | 'other'
58
+ /** Workspace-relative path. */
59
+ path: string
60
+ }
61
+
62
+ /** Project-level guidance the agent should read; never mixed into generic docs results. */
63
+ export interface GuidanceStatus {
64
+ sources: GuidanceSource[]
65
+ }
66
+
67
+ /** Optional-subsystem readiness. `not_provided` = no provider registered yet. */
68
+ export type SubsystemState = 'ready' | 'preparing' | 'stale' | 'unavailable' | 'not_provided'
69
+
70
+ export interface DocsStatus {
71
+ state: SubsystemState
72
+ /** e.g. which version binding is active, or why docs are unavailable. */
73
+ detail?: string
74
+ }
75
+
76
+ export interface CodeIndexStatus {
77
+ state: SubsystemState
78
+ detail?: string
79
+ /** Human-readable freshness marker (index revision vs working tree). */
80
+ freshness?: string
81
+ }
82
+
83
+ export type PrerequisiteStatus = 'satisfied' | 'missing' | 'unknown'
84
+
85
+ export interface RuntimePrerequisite {
86
+ /** Stable id: `node`, `git`, `pnpm`, `xcode`, `adb`, `nativescript-cli`, `chromium`, … */
87
+ id: string
88
+ name: string
89
+ status: PrerequisiteStatus
90
+ /** e.g. the found version, or where it was looked for. */
91
+ detail?: string
92
+ /** The smallest corrective action when missing/unknown. */
93
+ remedy?: string
94
+ /** Platforms this prerequisite gates; absent = server-wide. */
95
+ platforms?: Platform[]
96
+ }
97
+
98
+ /** The workspace as a whole. */
99
+ export type WorkspaceKind =
100
+ /** A consumer application workspace (single- or multi-app monorepo). */
101
+ | 'consumer'
102
+ /** The octane-xplat framework repository itself — framework sources are a separate corpus. */
103
+ | 'framework-repo'
104
+ /** No Octane Xplat app detected. */
105
+ | 'empty'
106
+
107
+ export interface WorkspaceInfo {
108
+ root: string
109
+ kind: WorkspaceKind
110
+ }
111
+
112
+ /**
113
+ * App selection state. `ambiguous` is a first-class state: the server never
114
+ * silently picks among multiple apps — the developer (via the agent) must choose.
115
+ */
116
+ export type AppSelection =
117
+ | { state: 'bound'; app: AppRef; reason: 'explicit' | 'sole-app' | 'configured' | 'preference' }
118
+ | { state: 'ambiguous'; candidates: AppRef[] }
119
+ | { state: 'none'; reason: 'no-apps' | 'workspace-not-found' }
120
+
121
+ export interface ProjectIdentity {
122
+ workspace: WorkspaceInfo
123
+ apps: AppRef[]
124
+ selection: AppSelection
125
+ /** Framework versions for the bound app (null while selection is ambiguous/none). */
126
+ framework: FrameworkBinding | null
127
+ }
@@ -0,0 +1,11 @@
1
+ export * from './errors'
2
+ export * from './identity'
3
+ export * from './capabilities'
4
+ export * from './policy'
5
+ export * from './sessions'
6
+ export * from './evidence'
7
+ export * from './boundaries'
8
+ export * from './status'
9
+ export * from './code'
10
+ export * from './actions'
11
+ export * from './inspection'
@@ -0,0 +1,45 @@
1
+ import type { EvidenceRecord } from './evidence'
2
+
3
+ /** Qualified scope of Chromium inspection; never a universal component tree. */
4
+ export const CHROMIUM_INSPECTION_COVERAGE: string
5
+
6
+ /** Select exactly one named DOM host. Duplicate names fail rather than choosing the first. */
7
+ export interface StableControl {
8
+ kind: 'id' | 'testID'
9
+ name: string
10
+ }
11
+
12
+ export interface InspectedHost {
13
+ id: string | null
14
+ testID: string | null
15
+ tag: string
16
+ role: string | null
17
+ /** Bounded to 500 characters; password and data-xplat-private hosts are redacted. */
18
+ text: string
19
+ value: string | null
20
+ checked: boolean | null
21
+ disabled: boolean
22
+ hidden: boolean
23
+ sensitive: boolean
24
+ focused: boolean
25
+ hitTarget: boolean
26
+ /** Main-document CSS pixels, relative to the viewport. */
27
+ frame: { x: number; y: number; width: number; height: number }
28
+ }
29
+
30
+ /** Returned by xplat_inspect; documentId must accompany subsequent interactions. */
31
+ export interface HostInspection {
32
+ documentId: string
33
+ controls: InspectedHost[]
34
+ coverage: string
35
+ limitations: string[]
36
+ total: number
37
+ nextOffset: number | null
38
+ evidence: EvidenceRecord
39
+ }
40
+
41
+ /** Input submission proves delivery only; observe and assert the intended state separately. */
42
+ export interface HostInteraction extends HostInspection {
43
+ inputSent: true
44
+ assertion: string
45
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Permission/policy contract. Workspace scope and ordinary runtime actions
3
+ * reuse the existing task authorization — policy is established once, not
4
+ * re-requested per tool call. Grants are scoped, revocable, optionally
5
+ * expiring, and checked at action time. `visual-capture` is never part of
6
+ * any baseline: the screenshot capability can exist while every actual
7
+ * capture still requires explicit per-task developer approval.
8
+ */
9
+
10
+ import type { ContractError } from './errors'
11
+
12
+ export const PERMISSION_SCOPES: readonly PermissionScope[]
13
+
14
+ export type PermissionScope =
15
+ /** Read the selected workspace: manifests, config, source inventory. */
16
+ | 'workspace'
17
+ /** Build, launch, attach, and interact with development sessions. */
18
+ | 'runtime-actions'
19
+ /** Screenshot capture and any visual inspection of captured pixels. Never granted by task authorization alone. */
20
+ | 'visual-capture'
21
+ /** Persist evidence artifacts beyond the MCP session. */
22
+ | 'evidence-retain'
23
+ /** Effects outside the app itself (external services, destructive resets). Never implied by runtime-actions. */
24
+ | 'external-effects'
25
+
26
+ export type GrantSource =
27
+ /** The developer authorized this task; covers workspace + runtime-actions only. */
28
+ | 'task-authorization'
29
+ /** An explicit developer approval for this task (e.g. via client elicitation). */
30
+ | 'developer'
31
+ /** A server startup flag/environment setting the developer configured. */
32
+ | 'server-config'
33
+
34
+ export type GrantState = 'granted' | 'denied' | 'required'
35
+
36
+ export interface PermissionGrant {
37
+ scope: PermissionScope
38
+ state: GrantState
39
+ source?: GrantSource
40
+ grantedAt?: string
41
+ /** ISO timestamp after which the grant no longer applies; absent = session-bound. */
42
+ expiresAt?: string
43
+ /** False only for server-internal baseline entries; everything the developer grants is revocable. */
44
+ revocable: boolean
45
+ note?: string
46
+ }
47
+
48
+ export type RequireResult =
49
+ | { granted: true; grant: PermissionGrant }
50
+ | { granted: false; error: ContractError }
51
+
52
+ /**
53
+ * The channel that asks the developer for a grant — wired to the client's
54
+ * elicitation support when available. Returns the resulting state.
55
+ */
56
+ export type GrantElicitor = (
57
+ scope: PermissionScope,
58
+ reason: string,
59
+ ) => Promise<'granted' | 'denied'> | 'granted' | 'denied'
60
+
61
+ export interface PolicyStoreOptions {
62
+ /** The developer authorized this task — grants workspace + runtime-actions for the session. */
63
+ taskAuthorization?: boolean
64
+ /** Extra grants from server configuration (env/flags), e.g. a pre-approved visual-capture. */
65
+ grants?: PermissionGrant[]
66
+ /** Optional elicitation channel used by `requestGrant`. */
67
+ elicit?: GrantElicitor
68
+ }
69
+
70
+ export interface PolicyStore {
71
+ /** Synchronous check used at action time. Expired grants are denied. */
72
+ require(scope: PermissionScope): RequireResult
73
+ /**
74
+ * Resolve a grant, asking the developer through the elicitation channel when
75
+ * one exists. Without a channel, returns `required` state plus the recovery
76
+ * text telling the developer how to grant it.
77
+ */
78
+ requestGrant(scope: PermissionScope, reason: string): Promise<RequireResult>
79
+ grant(scope: PermissionScope, grant?: Partial<PermissionGrant>): PermissionGrant
80
+ revoke(scope: PermissionScope): boolean
81
+ list(): PermissionGrant[]
82
+ /** Late-bind the elicitation channel (it needs a connected client). */
83
+ setElicitor(elicit: GrantElicitor): void
84
+ }
85
+
86
+ export declare function createPolicyStore(options?: PolicyStoreOptions): PolicyStore
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Session, job, and ownership contract.
3
+ *
4
+ * A session binds one app to one target. Sessions are either `owned` (this
5
+ * server started them — ending releases their resources) or `attached`
6
+ * (a development session the user already had running — ending detaches and
7
+ * preserves it). Restarting always establishes a new session identity; tools
8
+ * refuse operations against stopped or disconnected sessions.
9
+ *
10
+ * Jobs are bounded units of work (build, launch, dev-action, …) whose status
11
+ * is queryable without holding a tool call open. Repeating a request with the
12
+ * same `requestKey` returns the live job instead of duplicating it.
13
+ * Cancellation is cooperative: `cancelling` and `cancelled` are distinct
14
+ * states, and cancelling a job only signals that job's own operation — it
15
+ * must never kill an unrelated app.
16
+ */
17
+
18
+ import type { ContractError } from './errors'
19
+ import type { Platform } from './identity'
20
+
21
+ export const SESSION_STATES: readonly SessionState[]
22
+ export const JOB_STATES: readonly JobState[]
23
+ export const JOB_TERMINAL_STATES: readonly JobState[]
24
+
25
+ export type SessionState =
26
+ /** Bound but not yet ready for observation/interaction. */
27
+ | 'starting'
28
+ /** Ready — inspection, logs, and authorized interactions may proceed. */
29
+ | 'ready'
30
+ /** Lost contact; last-known data is historical evidence, never current state. */
31
+ | 'disconnected'
32
+ /** Ended — identity is stale; a new run is a new sessionId. */
33
+ | 'stopped'
34
+
35
+ export type SessionOwnership =
36
+ /** Started by this server; end releases owned resources. */
37
+ | 'owned'
38
+ /** Pre-existing user session; end detaches and preserves it. */
39
+ | 'attached'
40
+
41
+ /** Build/source identity attached to observations so a new install is never mistaken for the running process. */
42
+ export interface BuildIdentity {
43
+ buildId?: string
44
+ revision?: string
45
+ label?: string
46
+ }
47
+
48
+ export interface SessionBinding {
49
+ sessionId: string
50
+ appId: string
51
+ platform: Platform
52
+ targetId: string
53
+ ownership: SessionOwnership
54
+ state: SessionState
55
+ createdAt: string
56
+ /** `fresh` = new process; `warm` = iteration that retained process/module/OS state. */
57
+ processKind: 'fresh' | 'warm' | null
58
+ buildIdentity?: BuildIdentity
59
+ detail?: string
60
+ }
61
+
62
+ export interface EndSessionResult {
63
+ sessionId: string
64
+ ended: boolean
65
+ /** Owned resources released (processes, ports, temp dirs) — reported by the adapter. */
66
+ released?: string[]
67
+ /** What was deliberately left running — e.g. an attached user-owned session. */
68
+ preserved?: string[]
69
+ }
70
+
71
+ export type JobState = 'queued' | 'running' | 'succeeded' | 'failed' | 'cancelling' | 'cancelled'
72
+
73
+ export interface JobProgress {
74
+ phase?: string
75
+ percent?: number
76
+ message?: string
77
+ }
78
+
79
+ export interface Job {
80
+ jobId: string
81
+ /** e.g. `build`, `launch`, `attach`, `dev-action`. */
82
+ kind: string
83
+ sessionId?: string
84
+ /** Idempotence key supplied by the caller or adapter. */
85
+ requestKey?: string
86
+ state: JobState
87
+ progress?: JobProgress
88
+ result?: unknown
89
+ error?: ContractError
90
+ createdAt: string
91
+ finishedAt?: string
92
+ }
93
+
94
+ /** Adapter-provided work for a job. Receives the cooperative-cancel signal. */
95
+ export type JobRunner = (
96
+ signal: AbortSignal,
97
+ report: (progress: JobProgress) => void,
98
+ ) => Promise<unknown>
99
+
100
+ export interface StartJobOptions {
101
+ kind: string
102
+ sessionId?: string
103
+ requestKey?: string
104
+ run?: JobRunner
105
+ }
106
+
107
+ export interface StartJobResult {
108
+ job: Job
109
+ /** True when an identical live job already existed and was returned instead of starting a duplicate. */
110
+ deduplicated: boolean
111
+ }
112
+
113
+ export interface SessionRegistry {
114
+ /** Start a server-owned session in `starting` state. `cleanup` runs on end/shutdown and must only release resources this session created. */
115
+ bindOwned(input: {
116
+ appId: string
117
+ platform: Platform
118
+ targetId: string
119
+ cleanup?: () => Promise<{ released?: string[] } | void> | { released?: string[] } | void
120
+ detail?: string
121
+ }): SessionBinding
122
+ /** Bind an existing user-owned development session — never restarted or terminated by this server. */
123
+ attach(input: {
124
+ appId: string
125
+ platform: Platform
126
+ targetId: string
127
+ processKind?: 'fresh' | 'warm'
128
+ buildIdentity?: BuildIdentity
129
+ detail?: string
130
+ }): SessionBinding
131
+ get(sessionId: string): SessionBinding | null
132
+ /** The binding if usable, else a ContractError: `stale_identity` (stopped) or `disconnected_runtime`. */
133
+ requireUsable(
134
+ sessionId: string,
135
+ ): { usable: true; session: SessionBinding } | { usable: false; error: ContractError }
136
+ markReady(
137
+ sessionId: string,
138
+ meta?: { processKind?: 'fresh' | 'warm'; buildIdentity?: BuildIdentity },
139
+ ): SessionBinding | null
140
+ markDisconnected(sessionId: string, detail?: string): SessionBinding | null
141
+ end(sessionId: string): Promise<EndSessionResult>
142
+ /** End every owned session and detach attached ones. */
143
+ shutdown(): Promise<EndSessionResult[]>
144
+ list(): SessionBinding[]
145
+ }
146
+
147
+ export interface JobRegistry {
148
+ start(options: StartJobOptions): StartJobResult
149
+ status(jobId: string): Job | null
150
+ setProgress(jobId: string, progress: JobProgress): Job | null
151
+ succeed(jobId: string, result?: unknown): Job | null
152
+ fail(jobId: string, error: ContractError): Job | null
153
+ /** Cooperative cancellation; returns the job's resulting state. */
154
+ cancel(jobId: string): Job | null
155
+ /** Live and finished jobs (finished history is bounded). */
156
+ list(): Job[]
157
+ }
158
+
159
+ export declare function createSessionRegistry(): SessionRegistry
160
+ export declare function createJobRegistry(options?: { historyLimit?: number }): JobRegistry
@@ -0,0 +1,40 @@
1
+ /**
2
+ * The project-status report — the single externally observable shape returned
3
+ * by `xplat_project_status` at setup time. Later packages fill their own
4
+ * slots through registered providers; absent providers read as
5
+ * `not_provided`, never as healthy or silently absent.
6
+ */
7
+
8
+ import type {
9
+ CodeIndexStatus,
10
+ DocsStatus,
11
+ GuidanceStatus,
12
+ ProjectIdentity,
13
+ RuntimePrerequisite,
14
+ } from './identity'
15
+
16
+ import type { PlatformCapabilityMatrix } from './capabilities'
17
+ import type { PermissionGrant } from './policy'
18
+
19
+ export interface ServerInfo {
20
+ name: string
21
+ version: string
22
+ }
23
+
24
+ export interface ProjectStatusReport {
25
+ server: ServerInfo
26
+ identity: ProjectIdentity
27
+ guidance: GuidanceStatus
28
+ docs: DocsStatus
29
+ codeIndex: CodeIndexStatus
30
+ /** Prerequisites relevant to the detected apps' declared platforms. */
31
+ prerequisites: RuntimePrerequisite[]
32
+ /** Matrix for the workspace's declared platforms. */
33
+ capabilities: PlatformCapabilityMatrix[]
34
+ /** Current permission grants and their states. */
35
+ permissions: PermissionGrant[]
36
+ /** What this product deliberately does not do. */
37
+ boundaries: readonly string[]
38
+ /** Ordered next steps for the agent, tailored to the report's actual state. */
39
+ nextSteps: string[]
40
+ }