diff options
| author | Rider Linden <rider@lindenlab.com> | 2026-07-22 17:41:55 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-07-22 17:41:55 -0700 |
| commit | a6a2f98070cd55fcaadafc237bd0ffba9d466655 (patch) | |
| tree | 82f5444312786242e062de65ce6891213b032051 /doc | |
| parent | 8a22869dfad731f8cd9f4164a7a2b57cd70af51c (diff) | |
Publish object inventories to external editor. (#5834)
* Adding tight integration flag for vscode and open code through a URL.
* [WIP] Publish objects and their contents from the viewer into VS code.
* [WIP] Still very much a work in progress, supports most of the core operations publish, get, write, delete, and create. Still quite a few bugs to work out.
* [WIP] Object publishing checkpoint.
* [checkpoint] Script tight integration with vscode, object publishing.
* Number of fixed issue.
* A few redundancy and performance fixes.
* Some cosmetics.
* I like "Explore" better than "Publish"
* Object renaming, luau inventory icon, runstate, restart.
* Some clean up around permissions and possible nullptr deref.
* Code review feedback.
Diffstat (limited to 'doc')
| -rw-r--r-- | doc/external-editor-json-rpc.md | 694 |
1 files changed, 667 insertions, 27 deletions
diff --git a/doc/external-editor-json-rpc.md b/doc/external-editor-json-rpc.md index 9d355957fe..a3404ce1eb 100644 --- a/doc/external-editor-json-rpc.md +++ b/doc/external-editor-json-rpc.md @@ -5,12 +5,14 @@ This document describes all the message interfaces defined for WebSocket communi ## Table of Contents - [Usage Flow](#usage-flow) +- [VS Code Launch URI](#vs-code-launch-uri) - [JSON-RPC Method Summary](#json-rpc-method-summary) - [Session Management Interfaces](#session-management-interfaces) - [SessionHandshake](#sessionhandshake) - [SessionHandshakeResponse](#sessionhandshakeresponse) - [Session OK](#session-ok) - [SessionDisconnect](#sessiondisconnect) + - [SessionPing](#sessionping) - [Language and Syntax Interfaces](#language-and-syntax-interfaces) - [SyntaxChange](#syntaxchange) - [Language Syntax ID Request](#language-syntax-id-request) @@ -31,6 +33,19 @@ This document describes all the message interfaces defined for WebSocket communi - [Handler and Configuration Interfaces](#handler-and-configuration-interfaces) - [WebSocketHandlers](#websockethandlers) - [ClientInfo](#clientinfo) +- [Object Content Interfaces](#object-content-interfaces) + - [Core Data Types](#core-data-types) + - [ObjectPublish](#objectpublish) + - [ObjectUnpublish](#objectunpublish) + - [ObjectUpdate](#objectupdate) + - [ObjectContentGet](#objectcontentget) + - [ObjectContentSave](#objectcontentsave) + - [ObjectItemCreate](#objectitemcreate) + - [ObjectItemDelete](#objectitemdelete) + - [ObjectScriptSetRunning](#objectscriptsetrunning) + - [ObjectRequest](#objectrequest) + - [ObjectModify](#objectmodify) + - [ObjectItemModify](#objectitemmodify) ## Usage Flow @@ -53,42 +68,134 @@ This document describes all the message interfaces defined for WebSocket communi - When subscription needs to be terminated, viewer sends `script.unsubscribe` notification with `ScriptUnsubscribe` data - Extension handles unsubscription by cleaning up local script tracking -4. **Runtime Events:** +4. **Object Content Publishing:** + + - Viewer sends `object.publish` notification when an in-world object's contents are made available for editing + - Viewer sends `object.unpublish` notification when an object is removed or the owner stops publishing + - Viewer sends `object.update` notification when object inventory changes (full replacement or delta) + - Extension calls `object.content.get` to fetch an item's content on demand + - Extension calls `object.content.save` to write modified content back to the viewer + - Extension calls `object.item.create` / `object.item.delete` to manage inventory items + - Extension calls `object.script.set_running` to start or stop a script + +5. **Runtime Events:** - Viewer sends `language.syntax.change` notification with `SyntaxChange` when language changes - Viewer sends `script.compiled` notification with `CompilationResult` after script compilation - Viewer sends `runtime.debug` notification with `RuntimeDebug` for debug messages during script execution - Viewer sends `runtime.error` notification with `RuntimeError` when runtime errors occur -5. **Connection Termination:** +6. **Connection Termination:** - Either side can send `session.disconnect` notification with `SessionDisconnect` data - Connection is closed gracefully +## VS Code Launch URI + +The viewer can launch VS Code and trigger an automatic WebSocket connection by opening a `vscode://` URI via the operating system's default URI handler. The extension registers a URI handler for this scheme; VS Code will launch itself if not already running and deliver the URI to the extension. + +### URI Format + +``` +vscode://lindenlab.sl-vscode-plugin/connect[?port=<port>][&object=<uuid>][&script=<uuid>] +``` + +### Parameters + +| Parameter | Required | Description | +| --------- | -------- | ----------- | +| `port` | No | Port number the viewer's WebSocket server is listening on. Overrides the user's configured port for this session. Defaults to the configured `slVscodeEdit.network.websocketPort` (default `9020`) if absent. Must be in range 1024-65535. | +| `object` | No | UUID of a root prim. After the handshake completes the extension calls `object.request` to ask the viewer to publish this object. The viewer then sends an `object.publish` notification and the object appears as a workspace folder in the Explorer. | +| `script` | No | UUID of a script. After the handshake completes the extension locates the corresponding temp file via `script.list` and opens it, triggering the normal `script.subscribe` + live-sync flow. | + +`object` and `script` are mutually exclusive in typical use but both may be supplied; the extension will process both. + +### Examples + +``` +# Open VS Code and connect on default port +vscode://lindenlab.sl-vscode-plugin/connect + +# Connect on a custom port +vscode://lindenlab.sl-vscode-plugin/connect?port=9021 + +# Connect and immediately publish a specific object +vscode://lindenlab.sl-vscode-plugin/connect?port=9020&object=550e8400-e29b-41d4-a716-446655440000 + +# Connect and open a specific script for editing +vscode://lindenlab.sl-vscode-plugin/connect?port=9020&script=6ba7b810-9dad-11d1-80b4-00c04fd430c8 +``` + +### Post-connection sequence + +When the URI contains an `object` or `script` parameter the extension acts only **after** the handshake is fully complete (`session.ok` received): + +``` +URI received by extension + | + v +WebSocket connects -> session.handshake -> session.ok + | + |- object=<uuid> -> object.request({ object_id }) call + | | + | v (async, when viewer is ready) + | object.publish notification + | + \- script=<uuid> -> script.list call -> open temp file + | + v + script.subscribe + live-sync +``` + ## JSON-RPC Method Summary | Method | Direction | Type | Interface/Parameters | | ------------------------------- | ------------------ | ------------ | -------------------------- | -| `session.handshake` | Viewer → Extension | Call | `SessionHandshake` | -| `session.handshake` (response) | Extension → Viewer | Response | `SessionHandshakeResponse` | -| `session.ok` | Viewer → Extension | Notification | _(no interface)_ | +| `session.handshake` | Viewer -> Extension | Call | `SessionHandshake` | +| `session.handshake` (response) | Extension -> Viewer | Response | `SessionHandshakeResponse` | +| `session.ok` | Viewer -> Extension | Notification | _(no interface)_ | | `session.disconnect` | Bidirectional | Notification | `SessionDisconnect` | -| `script.subscribe` | Extension → Viewer | Call | `ScriptSubscribe` | -| `script.subscribe` (response) | Viewer → Extension | Response | `ScriptSubscribeResponse` | -| `script.unsubscribe` | Viewer → Extension | Notification | `ScriptUnsubscribe` | -| `script.list` | Extension → Viewer | Call | _(no parameters)_ | -| `script.list` (response) | Viewer → Extension | Response | `ScriptList` | -| `language.syntax.id` | Extension → Viewer | Call | _(no parameters)_ | -| `language.syntax.id` (response) | Viewer → Extension | Response | `{ id: string }` | -| `language.syntax` | Extension → Viewer | Call | `{ kind: string }` | -| `language.syntax` (response) | Viewer → Extension | Response | `LanguageInfo` | -| `language.syntax.cache` | Extension → Viewer | Call | _(no parameters)_ | -| `language.syntax.cache` (response) | Viewer → Extension | Response | `SyntaxCacheList` | -| `language.syntax.get` | Extension → Viewer | Call | `{ filename: string, as_json?: boolean }` | -| `language.syntax.get` (response) | Viewer → Extension | Response | `SyntaxCacheFile` | -| `language.syntax.change` | Viewer → Extension | Notification | `SyntaxChange` | -| `script.compiled` | Viewer → Extension | Notification | `CompilationResult` | -| `runtime.debug` | Viewer → Extension | Notification | `RuntimeDebug` | -| `runtime.error` | Viewer → Extension | Notification | `RuntimeError` | +| `session.ping` | Bidirectional | Call | `SessionPing` | +| `session.ping` (response) | Bidirectional | Response | `SessionPingResponse` | +| `script.subscribe` | Extension -> Viewer | Call | `ScriptSubscribe` | +| `script.subscribe` (response) | Viewer -> Extension | Response | `ScriptSubscribeResponse` | +| `script.unsubscribe` | Viewer -> Extension | Notification | `ScriptUnsubscribe` | +| `script.list` | Extension -> Viewer | Call | _(no parameters)_ | +| `script.list` (response) | Viewer -> Extension | Response | `ScriptList` | +| `language.syntax.id` | Extension -> Viewer | Call | _(no parameters)_ | +| `language.syntax.id` (response) | Viewer -> Extension | Response | `{ id: string }` | +| `language.syntax` | Extension -> Viewer | Call | `{ kind: string }` | +| `language.syntax` (response) | Viewer -> Extension | Response | `LanguageInfo` | +| `language.syntax.cache` | Extension -> Viewer | Call | _(no parameters)_ | +| `language.syntax.cache` (response) | Viewer -> Extension | Response | `SyntaxCacheList` | +| `language.syntax.get` | Extension -> Viewer | Call | `{ filename: string, as_json?: boolean }` | +| `language.syntax.get` (response) | Viewer -> Extension | Response | `SyntaxCacheFile` | +| `language.syntax.change` | Viewer -> Extension | Notification | `SyntaxChange` | +| `script.compiled` | Viewer -> Extension | Notification | `CompilationResult` | +| `runtime.debug` | Viewer -> Extension | Notification | `RuntimeDebug` | +| `runtime.error` | Viewer -> Extension | Notification | `RuntimeError` | +| `object.publish` | Viewer -> Extension | Notification | `ObjectPublishMessage` | +| `object.unpublish` | Viewer -> Extension | Notification | `ObjectUnpublishMessage` || `object.unpublish` | Extension → Viewer | Call | `ObjectUnpublishParams` | +| `object.unpublish` (response) | Viewer → Extension | Response | `ObjectUnpublishResponse` || `object.update` | Viewer -> Extension | Notification | `ObjectUpdateMessage` | +| `object.content.get` | Extension -> Viewer | Call | `ObjectContentGetParams` | +| `object.content.get` (response) | Viewer -> Extension | Response | `ObjectContentGetResponse` | +| `object.content.save` | Extension -> Viewer | Call | `ObjectContentSaveParams` | +| `object.content.save` (response)| Viewer -> Extension | Response | `ObjectContentSaveResponse`| +| `object.item.create` | Extension -> Viewer | Call | `ObjectItemCreateParams` | +| `object.item.create` (response) | Viewer -> Extension | Response | `ObjectItemCreateResponse` | +| `object.item.delete` | Extension -> Viewer | Call | `ObjectItemDeleteParams` | +| `object.item.delete` (response) | Viewer -> Extension | Response | `ObjectItemDeleteResponse` | +| `object.script.set_running` | Extension -> Viewer | Call | `ObjectScriptSetRunningParams` | +| `object.script.set_running` (response) | Viewer -> Extension | Response | `ObjectScriptSetRunningResponse` | +| `object.script.reset` | Extension -> Viewer | Call | `ObjectScriptResetParams` | +| `object.script.reset` (response)| Viewer -> Extension | Response | `ObjectScriptResetResponse` | +| `object.request` | Extension -> Viewer | Call | `ObjectRequestParams` | +| `object.request` (response) | Viewer -> Extension | Response | `ObjectRequestResponse` | +| `object.list` | Extension -> Viewer | Call | `{}` (no params) | +| `object.list` (response) | Viewer -> Extension | Response | `ObjectListResponse` | +| `object.modify` | Extension -> Viewer | Call | `ObjectModifyParams` | +| `object.modify` (response) | Viewer -> Extension | Response | `ObjectModifyResponse` | +| `object.item.modify` | Extension -> Viewer | Call | `ObjectItemModifyParams` | +| `object.item.modify` (response) | Viewer -> Extension | Response | `ObjectItemModifyResponse` | ## Session Management Interfaces @@ -188,6 +295,62 @@ interface SessionDisconnect { - `4`: Internal server error - `message`: Human-readable description of the disconnect reason +### SessionPing + +**JSON-RPC Method:** `session.ping` (call, bidirectional) + +Heartbeat call used to verify the connection is alive and measure latency. Either side can initiate a ping; the recipient responds with the original timestamp plus its own server time. + +```typescript +interface SessionPing { + timestamp: number; +} +``` + +**Fields:** + +- `timestamp`: Unix timestamp in milliseconds when the ping was sent + +**Response:** + +```typescript +interface SessionPingResponse { + timestamp: number; + server_time: number; +} +``` + +**Response Fields:** + +- `timestamp`: The original timestamp from the request (echoed back) +- `server_time`: Unix timestamp in milliseconds when the response was generated + +**Example Request:** + +```json +{ + "jsonrpc": "2.0", + "method": "session.ping", + "id": 42, + "params": { + "timestamp": 1721145600000 + } +} +``` + +**Example Response:** + +```json +{ + "jsonrpc": "2.0", + "id": 42, + "result": { + "timestamp": 1721145600000, + "server_time": 1721145600015 + } +} +``` + ## Language and Syntax Interfaces ### SyntaxChange @@ -293,7 +456,7 @@ interface SyntaxCacheList { | `lua_keywords_pretty.xml` | Luau keyword definitions in formatted LLSD XML format | | `secondlife_selene.yml` | Luau Selene linter configuration in YAML format | -Not all files may be present in every cache — the actual list returned by `language.syntax.cache` reflects only what is available on the viewer's local filesystem at the time of the request. +Not all files may be present in every cache - the actual list returned by `language.syntax.cache` reflects only what is available on the viewer's local filesystem at the time of the request. ### Language Syntax Cache Get @@ -382,9 +545,9 @@ interface ScriptSubscribeResponse { - `success`: Whether the subscription was successful - `status`: Numeric status code indicating the result: - `0`: Success - - `1`: Invalid editor — the script editor panel is no longer open - - `2`: Invalid subscription — no subscription found for the given `script_id` - - `3`: Already subscribed — another connection is already subscribed to this script + - `1`: Invalid editor - the script editor panel is no longer open + - `2`: Invalid subscription - no subscription found for the given `script_id` + - `3`: Already subscribed - another connection is already subscribed to this script - `4`: Internal server error - `object_id` (optional): The in-world UUID of the object containing the script - `item_id` (optional): The inventory item UUID of the script within the object @@ -522,7 +685,7 @@ interface RuntimeError { - `object_id`: Unique identifier for the object containing the script - `object_name`: Human-readable name of the object - `message`: The full raw chat text of the runtime error message as received from the simulator -- `error`: Extracted error description. Currently always an empty string — runtime error extraction from the simulator's multi-message format is not yet fully implemented. +- `error`: Extracted error description. Currently always an empty string - runtime error extraction from the simulator's multi-message format is not yet fully implemented. - `line`: Line number where the error occurred. Currently always `0` for the same reason. - `stack` (optional): Stack trace lines if they could be extracted from the error message @@ -578,3 +741,480 @@ interface ClientInfo { - `scriptId`: Unique identifier for the script - `extension`: File extension or script type +--- + +## Object Content Interfaces + +These interfaces support publishing in-world object inventories (scripts and notecards) to the external editor as a browseable virtual filesystem. The extension exposes published objects under the `sl://objects/` URI scheme. + +### Core Data Types + +```typescript +type InventoryItemType = "script" | "notecard"; + +type ScriptVM = "lsl2" | "mono" | "luau"; + +/** Permission mask fields. Only owner and next_owner are transmitted. */ +interface ItemPermissions { + owner: number; // e.g. PERM_MODIFY=0x4000, PERM_COPY=0x8000, PERM_TRANSFER=0x2000 + next_owner: number; +} + +/** + * Inventory item within an object or linked prim. + * asset_id is intentionally never transmitted. + */ +interface ObjectInventoryItem { + item_id: string; // Inventory item UUID + name: string; // Display name (no file extension) + description?: string; + type: InventoryItemType; + subtype?: number; // Scripts only: language from II_FLAGS_SUBTYPE_MASK (0=LSL, 1=Luau) + vm?: ScriptVM; // Scripts only: which VM the script targets + running?: boolean; // Scripts only: whether the script is running + faulted?: boolean; // Scripts only: whether the script has a runtime fault + permissions?: ItemPermissions; + creator_id?: string; +} + +/** A linked (child) prim within a linkset */ +interface LinkedObject { + link_id: string; // UUID of the linked prim + link_number: number; // Link number (root=1, children>=2) + link_name: string; + link_description?: string; + inventory: ObjectInventoryItem[]; +} + +interface ObjectPermissions { + owner: number; + next_owner: number; +} + +/** Root of a linkset, as published to the extension */ +interface PublishedObject { + object_id: string; // UUID of the root prim + object_name: string; + object_description?: string; + region?: string; + owner_id?: string; + permissions?: ObjectPermissions; + inventory: ObjectInventoryItem[]; // Root prim's scripts and notecards + linked_objects?: LinkedObject[]; // Child prims +} +``` + +**Script display extensions** (synthetic, derived from `subtype`): + +| `subtype` | Extension | +| --------- | --------- | +| `0` (LSL) | `.lsl` | +| `1` (Luau)| `.luau` | +| notecard | `.txt` | + +--- + +### ObjectPublish + +**JSON-RPC Method:** `object.publish` (notification from viewer) + +Sent when the viewer publishes an in-world object's inventory for external editing. Triggers creation of a virtual filesystem workspace folder in the extension. + +```typescript +interface ObjectPublishMessage { + object: PublishedObject; +} +``` + +**Fields:** + +- `object`: The full published object tree, including root prim inventory and all linked prim inventories. + +--- + +### ObjectUnpublish + +**JSON-RPC Method:** `object.unpublish` (notification from viewer) + +Sent when the viewer removes a previously published object - for example when the owner deselects it, moves away, or the object is deleted. + +```typescript +interface ObjectUnpublishMessage { + object_id: string; + reason?: string; +} +``` + +**Fields:** + +- `object_id`: UUID of the root prim that is being unpublished +- `reason` (optional): Human-readable explanation (e.g. `"object deleted"`, `"out of range"`) + +**JSON-RPC Method:** `object.unpublish` (call from extension to viewer) + +The extension may also call `object.unpublish` to manually stop tracking an object. The viewer will stop publishing it and send a corresponding `object.unpublish` notification back to the caller. + +```typescript +interface ObjectUnpublishParams { + object_id: string; // UUID of the root prim to unpublish +} + +interface ObjectUnpublishResponse { + success: boolean; + object_id?: string; +} +``` + +**Fields:** + +- `object_id`: UUID of the root prim to unpublish. +- `success`: `true` if the object was published and has been removed. + +**Note:** The viewer also sends an `object.unpublish` notification to the caller immediately after responding. Extensions should handle that notification idempotently. + +--- + +### ObjectUpdate + +**JSON-RPC Method:** `object.update` (notification from viewer) + +Sent when the inventory of a published object changes. Supports two modes: +- **Full replacement**: `inventory` and/or `linked_objects` fields replace the entire prior state. +- **Delta update**: `changes` field describes only what changed. Takes precedence over full replacement fields when present. + +```typescript +interface InventoryChanges { + added?: ObjectInventoryItem[]; + removed?: string[]; // item_ids removed + modified?: ObjectInventoryItem[]; // metadata-only changes + content_changed?: string[]; // item_ids whose content changed (invalidates cache) + running_changed?: { item_id: string; running: boolean }[]; // running state toggled +} + +interface LinkedObjectChanges { + added?: LinkedObject[]; + removed?: string[]; // link_ids removed + modified?: { + link_id: string; + link_name?: string; + inventory?: InventoryChanges; + }[]; +} + +interface ObjectUpdateMessage { + object_id: string; + object_name?: string; + // Full replacement (used when changes is absent) + inventory?: ObjectInventoryItem[]; + linked_objects?: LinkedObject[]; + // Delta (takes precedence when present) + changes?: { + inventory?: InventoryChanges; + linked_objects?: LinkedObjectChanges; + }; +} +``` + +--- + +### ObjectContentGet + +**JSON-RPC Method:** `object.content.get` (call from extension to viewer) + +Requests the text content of a script or notecard. The extension calls this lazily when the user opens a file in the virtual filesystem. + +```typescript +interface ObjectContentGetParams { + prim_id: string; // UUID of any prim (root or child) - no object_id + link_id needed + item_id: string; +} + +interface ObjectContentGetResponse { + success: boolean; + prim_id: string; + item_id: string; + content: string; // Raw text content (UTF-8). Notecard envelope is unwrapped automatically. +} +``` + +**Fields:** + +- `prim_id`: UUID of the prim that owns the item. Child prims are addressable directly by UUID without knowing the root object_id. +- `item_id`: Inventory item UUID. +- `success`: `true` on success. +- `content`: The raw text content of the item. For notecards, the `Linden text version 2` envelope is stripped - only the body text is returned. + +--- + +### ObjectContentSave + +**JSON-RPC Method:** `object.content.save` (call from extension to viewer) + +Writes modified content back to the viewer. For scripts, the viewer will attempt to compile the updated source. + +```typescript +interface ObjectContentSaveParams { + prim_id: string; + item_id: string; + content: string; + vm?: "mono" | "lsl2" | "luau"; +} + +interface ObjectContentSaveResponse { + success: boolean; + prim_id?: string; + item_id?: string; + compiled?: boolean; + errors?: string[]; + message?: string; +} +``` + +**Fields:** + +- `prim_id`: UUID of the prim that owns the saved item. +- `item_id`: UUID of the saved inventory item. +- `content`: Raw script/notecard source text to store. +- `vm` (optional): Scripts only compile target. Accepted values are `"mono"`, `"lsl2"`, `"luau"`. When `"luau"` is specified for an LSL script (as opposed to a native Luau script), the viewer automatically selects the correct LSL-on-Luau compile path. If omitted, inferred from item metadata or content analysis. +- `success`: Whether the upload/save operation succeeded. +- `compiled` (optional): Scripts only. `true` when compilation succeeded, `false` when source saved but compile failed. +- `errors` (optional): Scripts only. Compiler diagnostics when `compiled` is `false`. +- `message` (optional): Error description on failure. + +--- + +### ObjectItemCreate + +**JSON-RPC Method:** `object.item.create` (call from extension to viewer) + +Creates a new script in a prim's inventory. The call is asynchronous - the viewer sends +`RezScript` to the simulator and waits for the inventory-changed callback before returning +the created item's details. The simulator may rename the item if a duplicate name exists. + +Notecard creation is not yet supported and will return an error. + +```typescript +interface ObjectItemCreateParams { + prim_id: string; // UUID of the prim to create the item in + name: string; // Pure SL inventory name - no file extension + type: InventoryItemType; // "script" ("notecard" reserved for future) + vm: ScriptVM; // Required for scripts: "luau" | "mono" | "lsl2" +} + +// On success, returns an ObjectInventoryItem with prim_id: +interface ObjectItemCreateResponse extends ObjectInventoryItem { + prim_id: string; // Echoed prim UUID +} +``` + +**Notes:** +- The response matches the `ObjectInventoryItem` structure (same fields as items in + `object.publish` and `object.update` notifications). +- The `name` in the response may differ from the request if the simulator renamed it. +- An `object.update` notification will also fire for the prim (since inventory changed). +- Timeout: 30 seconds. Returns a JSON-RPC internal error if the simulator does not respond. + +--- + +### ObjectItemDelete + +**JSON-RPC Method:** `object.item.delete` (call from extension to viewer) + +Deletes a script or notecard from a prim's inventory. Requires `PERM_MODIFY` on the item. + +```typescript +interface ObjectItemDeleteParams { + prim_id: string; + item_id: string; +} + +interface ObjectItemDeleteResponse { + success: boolean; + prim_id: string; // Echoed back from request + item_id: string; // Echoed back from request +} +``` + +--- + +### ObjectScriptSetRunning + +**JSON-RPC Method:** `object.script.set_running` (call from extension to viewer) + +Starts or stops a script within a prim. + +```typescript +interface ObjectScriptSetRunningParams { + prim_id: string; + item_id: string; + running: boolean; // true = start, false = stop +} + +interface ObjectScriptSetRunningResponse { + success: boolean; + message?: string; +} +``` + +--- + +### ObjectScriptReset + +**JSON-RPC Method:** `object.script.reset` (call from extension to viewer) + +Resets a script within a prim, clearing its state and restarting from the default state entry. + +```typescript +interface ObjectScriptResetParams { + prim_id: string; + item_id: string; +} + +interface ObjectScriptResetResponse { + success: boolean; + message?: string; +} +``` + +--- + +### ObjectRequest + +**JSON-RPC Method:** `object.request` (call from extension to viewer) + +Requests the viewer to publish a specific in-world object. The viewer responds synchronously to confirm the request was accepted, then asynchronously sends an `object.publish` notification with the full object tree. + +This is typically called immediately after the handshake completes when the extension was launched by the viewer with an `object=<uuid>` URI parameter. + +```typescript +interface ObjectRequestParams { + object_id: string; // UUID of the root prim to request publishing for +} + +interface ObjectRequestResponse { + success: boolean; + message?: string; // reason on failure (e.g. "object not found", "permission denied") +} +``` + +**Fields:** + +- `object_id`: UUID of the root prim of the linkset to publish. +- `success`: Whether the viewer accepted the request. A `true` response does not mean `object.publish` has been sent yet - it means the viewer will send it. +- `message` (optional): Human-readable failure reason. Only present when `success` is `false`. + +**Sequence:** +1. Extension calls `object.request` +2. Viewer responds with `{ success: true }` (or error) +3. Viewer sends `object.publish` notification (asynchronously, when ready) + +--- + +### ObjectList + +**JSON-RPC Method:** `object.list` (call from extension to viewer) + +Requests the complete list of currently published objects. Called by the extension immediately after the handshake completes (`session.ok`) to restore state for any objects the viewer already has published. + +The viewer responds synchronously with all published objects in the same format as `object.publish` notifications. No follow-up notifications are sent. + +```typescript +// No request parameters + +interface ObjectListResponse { + objects: PublishedObject[]; // All currently published objects; empty array if none +} +``` + +**Fields:** + +- `objects`: Array of `PublishedObject` records (same shape as the `object` field in `object.publish`). Empty array when no objects are currently published. + +**Sequence:** +1. Viewer sends `session.ok` +2. Extension calls `object.list` (no params) +3. Viewer responds with `{ objects: [...] }` synchronously + +--- + +### ObjectModify + +**JSON-RPC Method:** `object.modify` (call from extension to viewer) + +Modifies properties of a prim (root or linked) such as name, description, or permissions. Only specified fields are modified; omitted fields remain unchanged. Requires `PERM_MODIFY` on the object. + +```typescript +interface ObjectModifyParams { + prim_id: string; // UUID of any prim (root or child) + name?: string; // New display name + description?: string; // New description + permissions?: { + next_owner?: number; // Permission mask applied on transfer + }; +} + +interface ObjectModifyResponse { + success: boolean; + prim_id: string; // Echoed back from request + message?: string; // Error description on failure +} +``` + +**Fields:** + +- `prim_id`: UUID of the prim to modify. Child prims are addressable directly by UUID. +- `name` (optional): New display name for the prim. If omitted, name remains unchanged. +- `description` (optional): New description for the prim. If omitted, description remains unchanged. +- `permissions` (optional): Permission changes. + - `next_owner`: Permission mask applied when the object is transferred. Uses same bit flags as `ItemPermissions` (e.g., `PERM_MODIFY=0x4000`, `PERM_COPY=0x8000`, `PERM_TRANSFER=0x2000`). +- `success`: Whether the update operation succeeded. +- `message` (optional): Error description. Only present when `success` is `false`. + +**Notes:** +- At least one property field (`name`, `description`, or `permissions`) must be specified. +- An `object.update` notification will fire after successful modification. +- Owner permissions cannot be modified directly — only `next_owner` can be changed. + +--- + +### ObjectItemModify + +**JSON-RPC Method:** `object.item.modify` (call from extension to viewer) + +Modifies properties of an inventory item such as name, description, or permissions. Only specified fields are modified; omitted fields remain unchanged. Requires `PERM_MODIFY` on the item. + +```typescript +interface ObjectItemModifyParams { + prim_id: string; // UUID of any prim (root or child) + item_id: string; // Inventory item UUID + name?: string; // New display name (no file extension) + description?: string; // New description + permissions?: { + next_owner?: number; // Permission mask applied on transfer + }; +} + +interface ObjectItemModifyResponse { + success: boolean; + prim_id: string; // Echoed back from request + item_id: string; // Echoed back from request + message?: string; // Error description on failure +} +``` + +**Fields:** + +- `prim_id`: UUID of the prim that owns the item. Child prims are addressable directly by UUID. +- `item_id`: Inventory item UUID. +- `name` (optional): New display name for the item. Should not include file extension (e.g., `.lsl`, `.luau`). If omitted, name remains unchanged. +- `description` (optional): New description for the item. If omitted, description remains unchanged. +- `permissions` (optional): Permission changes. + - `next_owner`: Permission mask applied when the item is transferred. Uses same bit flags as `ItemPermissions` (e.g., `PERM_MODIFY=0x4000`, `PERM_COPY=0x8000`, `PERM_TRANSFER=0x2000`). +- `success`: Whether the update operation succeeded. +- `message` (optional): Error description. Only present when `success` is `false`. + +**Notes:** +- At least one property field (`name`, `description`, or `permissions`) must be specified. +- An `object.update` notification will fire after successful modification. +- Owner permissions cannot be modified directly — only `next_owner` can be changed. +- If the item is renamed, the virtual filesystem path will change and the extension must handle the rename appropriately. |
