Skip to content

Build and Publish ​

Learn how to build your LeftUnread scenario and publish it to a server.

Building Your Scenario ​

The build process compiles your Ink scripts, packages assets, and creates a deployable archive.

Build Command ​

bash
npm run build:scenario

What Happens During Build ​

  1. Finds Ink Files - Scans src/ directory for all .ink files
  2. Compiles Ink to JSON - Converts Ink scripts to JSON format using inkjs or inklecate
  3. Copies Assets - Copies all assets from src/ to build directory (excluding .ink files)
  4. Generates Manifests - Creates manifest files listing all compiled Ink scripts
  5. Creates Archive - Packages everything into dist/scenario-build.zip
  6. Cleanup - Removes temporary build directory

Build Output ​

After a successful build:

dist/
└── scenario-build.zip    # Your packaged scenario

The archive contains:

  • Compiled Ink scripts (.json files)
  • All assets (images, avatars)
  • Contact definitions
  • Conversation configurations
  • Scenario metadata
  • Manifest files

Build Logs ​

The build script provides detailed output:

bash
Found 3 .ink files:
  - src/ink/alex.ink
  - src/ink/mum.ink
  - src/ink/family-group.ink

Compiling src/ink/alex.ink -> dist/build/ink/alex.json
✅ Successfully compiled alex.ink
Compiling src/ink/mum.ink -> dist/build/ink/mum.json
✅ Successfully compiled mum.ink
Compiling src/ink/family-group.ink -> dist/build/ink/family-group.json
✅ Successfully compiled family-group.ink

🎉 Ink compile: 3/3 files compiled successfully
🗂️  Wrote root manifest: dist/build/manifest.json
🗜️  Zipped build to dist/scenario-build.zip
🧹 Removed temporary build directory: dist/build

✅ Build complete. Archive located at: dist/scenario-build.zip

Testing Your Scenario Locally ​

Before publishing, you can test your scenario in a browser using the local development server:

bash
npm run run:scenario

This will:

  • Start a local development server
  • Open your scenario in your browser at https://localhost:3000
  • Automatically deploy changes when you save files
  • Allow you to interactively test conversations and player choices

How Local Testing Works ​

  1. Start the server - Run npm run run:scenario
  2. Browser opens - Navigate to https://localhost:3000
  3. Make changes - Edit your Ink files, contacts, or conversations
  4. Save files - Changes are automatically deployed
  5. Restart scenario - Restart the scenario in the browser to see your changes

Development Workflow

Use npm run run:scenario for interactive testing, then use npm run build:scenario when you're ready to create the production build.

::: note Restart Required After saving changes, you'll need to restart the scenario session in the browser to see updates. The changes are deployed automatically, but each scenario session needs to be restarted to load the new version. :::

Watch Mode ​

For active development, use watch mode to automatically rebuild when files change:

bash
npm run build:scenario -- --watch

This will:

  • Build your scenario initially
  • Watch the src/ directory for changes
  • Automatically rebuild when you save files
  • Keep running until you press Ctrl+C

Development Workflow Options

  • Interactive Testing: Use npm run run:scenario to test in browser
  • Watch Mode: Use npm run build:scenario -- --watch for automatic rebuilds
  • Both: Run watch mode in one terminal and run:scenario in another for the best development experience

Ink Compilation ​

Compiler Options ​

The build script tries multiple compilers in order:

  1. inkjs (preferred) - JavaScript-based Ink compiler
  2. inklecate (fallback) - Official Ink compiler
  3. Basic JSON (last resort) - Creates simple JSON structure

Installing Compilers ​

inkjs (included in dependencies):

bash
npm install

inklecate (optional, for better compatibility):

bash
npm install -g inklecate

Compilation Errors ​

If your Ink has syntax errors, the build will fail with error messages:

bash
❌ Failed to compile src/ink/alex.ink:
Error: Expected '->' or 'END' but got 'invalid'
Line 42: invalid syntax here

Common Issues:

  • Missing -> END at the end of knots
  • Undefined knot references
  • Syntax errors in conditions
  • Unclosed brackets or quotes

Debugging Ink Scripts

Best Practice: Use Visual Studio Code with the Ink extension for writing:

  • Install the Ink extension in VS Code (search "Ink" by inkle in Extensions)
  • Get syntax highlighting and error detection as you type
  • Catch errors before building

Alternative: Use the Inky editor to test your Ink scripts before building.

Publishing to Server ​

Once your scenario is built, you can publish it to a LeftUnread server.

Prerequisites ​

Before you can publish, you need to set up your scenario and get your credentials.

Step 1: Create Your Scenario on the Server ​

  1. Visit the Scenarios Management Page
  2. Click the button to create a new scenario
  3. Fill in your scenario information:
    • Scenario name
    • Description
    • Age rating
    • Difficulty settings
  4. After creating the scenario, look at your browser's URL bar
  5. Copy the UUID (the long string of letters and numbers at the end)
    • Example URL: https://leftunread.app/authors/test/scenarios/abc123-def456-ghi789
    • The UUID is: abc123-def456-ghi789
    • This is your scenario ID (set SCENARIO_ID in .env)

Step 2: Get Your API Key ​

  1. Visit the Manage Passport Tokens Page
  2. Create a new API token
    • Give it a descriptive name (e.g., "My Scenario Publishing Key")
  3. Copy the API key immediately - you won't be able to see it again!
  4. This is your API_KEY

Save Your API Key!

Make sure to copy and save your API key somewhere safe. If you lose it, you'll need to create a new one.

Step 3: Configure Your Environment File ​

  1. In your project folder, copy the example file:

    bash
    cp .env.example .env
  2. Open the .env file in any text editor (Notepad, VS Code, etc.)

  3. Fill in your credentials:

    env
    BASE_URL=https://leftunread.app
    SCENARIO_ID=paste-your-scenario-uuid-here
    API_KEY=paste-your-api-key-here
  4. Save the file

Security Warning

Never commit .env to version control! It contains sensitive API keys. Make sure .env is in your .gitignore file.

For Non-Technical Users

Think of the .env file as your personal login credentials for publishing. Keep it safe and don't share it with anyone!

Publish Command ​

bash
npm run publish:scenario

What Happens During Publish ​

  1. Builds Scenario - Runs npm run build:scenario automatically
  2. Validates Archive - Checks that dist/scenario-build.zip exists
  3. Reads Credentials - Loads BASE_URL, API_KEY, and SCENARIO_ID (scenario UUID) from .env
  4. Uploads Archive - Sends zip file to server via HTTP POST
  5. Shows Response - Displays server response with version information

Publish Output ​

Successful publish:

bash
🔨 Building scenario...
[build output...]
✅ Build complete. Archive located at: dist/scenario-build.zip

📤 Uploading dist/scenario-build.zip -> https://server.com/api/scenarios/my-scenario/versions

✅ Upload complete. Server response:
{
  "id": "my-scenario-v2",
  "version": "1.1.0",
  "scenarioId": "my-scenario",
  "createdAt": "2025-10-18T14:30:00Z"
}

Publish Errors ​

Missing Environment Variables:

bash
Missing environment variable: API_KEY

Solution: Check your .env file has all required variables.

Build Failed:

bash
Build artifact not found: dist/scenario-build.zip

Solution: Fix build errors before publishing.

Upload Failed:

bash
❌ Upload failed: 401 Unauthorized

Solution: Check your API key is correct.

Server Error:

bash
❌ Upload failed: 400 Bad Request
Invalid scenario format

Solution: Ensure your scenario structure is correct.

Version Management ​

Scenario Versions ​

Each time you publish, a new version is created on the server. The server tracks:

  • Version number (e.g., "1.0.0", "1.1.0")
  • Upload timestamp
  • Scenario content (the zip file)

Updating Your Scenario ​

To release an update:

  1. Make Changes - Edit your Ink files, assets, or conversation configurations
  2. Test Locally - Build and verify everything works
  3. Publish - Run npm run publish:scenario

Each time you publish, a new version is automatically created on the server with an incremented version number.

Build Script Details ​

Script Location ​

The build script is at scripts/build-scenario.cjs.

Key Functions ​

findInkFiles(dir) - Recursively finds all .ink files

compileInkFile(inkPath) - Compiles a single Ink file to JSON

copyDirSync(src, dest) - Copies directory contents (excluding .ink files)

zipDirectory(sourceDir, outPath) - Creates zip archive

removeInkFiles(dir) - Removes .ink files from build (keeps only compiled JSON)

buildAllStories() - Main build function

Customizing the Build ​

You can modify scripts/build-scenario.cjs to:

  • Change output directory
  • Add custom validation
  • Include additional files
  • Modify manifest generation

Publish Script Details ​

Script Location ​

The publish script is at scripts/publish-scenario.cjs.

Key Functions ​

loadDotEnv(envPath) - Loads environment variables from .env

requireEnv(name) - Validates required environment variable exists

main() - Main publish function

API Endpoint ​

The script uploads to:

POST {BASE_URL}/api/scenarios/{SCENARIO_ID}/versions

SCENARIO_ID in .env is your scenario UUID. The API path is /api/scenarios/…. Older tools may still post to /api/threads/… or /api/stories/… — if publish 404s after a tools upgrade, confirm the script posts to /api/scenarios.

Headers:

  • Authorization: Bearer {API_KEY}
  • Accept: application/json

Body:

  • Multipart form data with file field containing the zip

Customizing the Publish ​

You can modify scripts/publish-scenario.cjs to:

  • Add custom headers
  • Change upload endpoint
  • Add pre-publish validation
  • Implement retry logic

Troubleshooting ​

Build Issues ​

"No .ink files found"

  • Check that .ink files are in src/ink/ directory
  • Verify file extensions are .ink not .txt

"Failed to compile"

  • Check Ink syntax in the failing file
  • Test in Inky editor
  • Look for missing -> END statements

"Failed to zip directory"

  • Ensure zip command is available on your system
  • Check disk space
  • Verify write permissions on dist/ directory

Publish Issues ​

"Missing environment variable"

  • Create .env file from .env.example
  • Fill in all required values
  • Check for typos in variable names

"curl is required"

  • Install curl: sudo apt install curl (Linux) or brew install curl (macOS)
  • Or modify script to use Node.js HTTP client

"401 Unauthorized"

  • Verify API key is correct
  • Check API key hasn't expired
  • Ensure API key has permission to upload scenarios

"404 Not Found"

  • Verify BASE_URL is correct
  • Check the scenario UUID in SCENARIO_ID exists on the server
  • Ensure scenario was created on server first

Best Practices ​

Development Workflow ​

  1. Edit - Make changes to your scenario files
  2. Test Locally - Run npm run run:scenario to test in browser (optional but recommended)
  3. Build - Run npm run build:scenario to test compilation
  4. Verify - Check for errors and test the build
  5. Iterate - Fix any issues and rebuild
  6. Publish - When ready, run npm run publish:scenario

Use Watch Mode ​

During active development:

bash
# Terminal 1: Watch mode
npm run build:scenario -- --watch

# Terminal 2: Edit your files
# Builds happen automatically on save

Version Control ​

Commit to Git:

  • Source files (src/)
  • Scripts (scripts/)
  • Configuration (package.json, .env.example)

Don't Commit:

  • Build output (dist/)
  • Environment variables (.env)
  • Dependencies (node_modules/)

.gitignore example:

.env
node_modules/
dist/

Testing Before Publishing ​

  1. Build locally - Ensure no compilation errors
  2. Check file size - Large zips may fail to upload
  3. Verify assets - All images and files are included
  4. Test scenario flow - Walk through the narrative
  5. Update version - Increment version number
  6. Publish - Upload to server

Automated Deployment with GitHub Actions ​

Want to automatically build and publish your scenario every time you push to GitHub?

See the GitHub Actions Guide for step-by-step instructions on setting up automatic deployment.

Benefits:

  • ⚡ Automatic builds on every push
  • 🔒 Secure credential storage
  • 📝 Deployment history tracking
  • 🤝 Team collaboration without sharing API keys

Next Steps ​

Released under the MIT License.