Lybic Docs

Run long-running asynchronous tasks

Run long-running tasks in a Windows sandbox with PowerShell background jobs

This document applies to Windows sandboxes. If you are using a Linux sandbox, see Run long-running tasks in a Linux sandbox.

Tip: For quick commands that finish within 5 seconds, use the execSandboxProcess API.

How it works

On Windows, there are three main ways to run long-running background tasks:

  1. PowerShell Jobs (Start-Job): simplest; good for short-to-medium background tasks
  2. Scheduled Tasks (schtasks): most reliable; good for long-running and persistent tasks
  3. Start-Process: good for simple background process launches, but weaker monitoring

This document focuses on the first two approaches.

PowerShell Jobs are the simplest and most direct option, suitable for tasks that run from minutes to hours.

Step overview

  1. Prepare a script or program: transfer the script/program to the sandbox
  2. Start the task: launch it in the background with Start-Job
  3. Check status: query the task status and results

Step 1: Prepare a script or program

There are two ways to prepare the script or program you want to run:

Option A: Copy files into the sandbox

Use the File transfer API to upload the script into the sandbox:

# First, upload the script to object storage (get a download URL)
# Then, download from the URL into the sandbox
curl -X POST "https://api.lybic.cn/api/orgs/{orgId}/sandboxes/{sandboxId}/file/copy" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"files": [
{
"id": "upload-training-script",
"src": {
"type": "httpGetLocation",
"url": "https://storage.example.com/scripts/train.ps1"
},
"dest": {
"type": "sandboxFileLocation",
"path": "C:\\Users\\Administrator\\scripts\\train.ps1"
}
}
]
}'
import asyncio
from lybic import LybicClient, LybicAuth
from lybic.dto import (
SandboxFileCopyRequestDto,
FileCopyItem,
SandboxFileLocation,
HttpGetLocation
)

async def main():
async with LybicClient(
LybicAuth(
org_id="ORG-xxxx",
api_key="lysk-xxxxxxxxxxx",
endpoint="https://api.lybic.cn/"
)
) as client:
# Upload the script into the sandbox
await client.sandbox.copy_files(
"SBX-xxxx",
SandboxFileCopyRequestDto(files=[
FileCopyItem(
id="upload-training-script",
src=HttpGetLocation(
url="https://storage.example.com/scripts/train.ps1"
),
dest=SandboxFileLocation(
path="C:\\Users\\Administrator\\scripts\\train.ps1"
)
)
])
)
print("Script uploaded successfully")

if __name__ == "__main__":
asyncio.run(main())
import { LybicClient } from '@lybic/core'

const lybic = new LybicClient({
baseUrl: 'https://api.lybic.cn',
orgId: 'ORG-xxxx',
apiKey: 'lysk-your-api-key-here',
})

// Upload the script into the sandbox
await lybic.copyFilesWithSandbox('SBX-xxxx', {
files: [
{
id: 'upload-training-script',
src: {
type: 'httpGetLocation',
url: 'https://storage.example.com/scripts/train.ps1',
},
dest: {
type: 'sandboxFileLocation',
path: 'C:\\Users\\Administrator\\scripts\\train.ps1',
},
},
],
})

console.log('Script uploaded successfully')
package main

import (
"context"
"fmt"
"github.com/lybic/lybic-sdk-go"
)

func main() {
ctx := context.Background()
client, _ := lybic.NewClient(nil)

copyDto := lybic.SandboxFileCopyRequestDto{
Files: []lybic.SandboxFileCopyRequestDtoFiles{
{
Id: "upload-training-script",
Src: map[string]string{
"type": "httpGetLocation",
"url":  "https://storage.example.com/scripts/train.ps1",
},
Dest: map[string]any{
"type": "sandboxFileLocation",
"path": "C:\\Users\\Administrator\\scripts\\train.ps1",
},
},
},
}

_, err := client.CopyFilesWithSandbox(ctx, "SBX-xxxx", copyDto)
if err != nil {
fmt.Println("Error uploading script:", err)
return
}
fmt.Println("Script uploaded successfully")
}

Option B: Write via stdin

Use execSandboxProcess to pipe the script content into a file:

# Base64-encode the script content
SCRIPT_CONTENT=$(cat << 'EOF' | base64 -w0
Write-Host "Starting model training..."
for ($i = 1; $i -le 100; $i++) {
Start-Sleep -Seconds 1
Write-Host "Progress: $i/100"
}
Write-Host "Training complete!"
EOF
)

# Write to file
curl -X POST "https://api.lybic.cn/api/orgs/{orgId}/sandboxes/{sandboxId}/process" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"executable": "powershell.exe",
"args": ["-Command", "$input | Out-File -FilePath C:\\Users\\Administrator\\scripts\\train.ps1 -Encoding UTF8"],
"stdinBase64": "'$SCRIPT_CONTENT'"
}'
import asyncio
import base64
from lybic import LybicClient, LybicAuth

async def main():
async with LybicClient(
LybicAuth(
org_id="ORG-xxxx",
api_key="lysk-xxxxxxxxxxx",
endpoint="https://api.lybic.cn/"
)
) as client:
# Prepare the script content
script_content = """Write-Host "Starting model training..."
for ($i = 1; $i -le 100; $i++) {
Start-Sleep -Seconds 1
Write-Host "Progress: $i/100"
}
Write-Host "Training complete!"
"""

# Base64 encode
stdin_data = base64.b64encode(script_content.encode('utf-8')).decode()

# Write to file
result = await client.sandbox.execute_process(
"SBX-xxxx",
executable="powershell.exe",
args=["-Command", "$input | Out-File -FilePath C:\\Users\\Administrator\\scripts\\train.ps1 -Encoding UTF8"],
stdinBase64=stdin_data
)

if result.exitCode == 0:
print("Script created successfully")
else:
print(f"Create failed: {base64.b64decode(result.stderrBase64 or '').decode()}")

if __name__ == "__main__":
asyncio.run(main())
import { LybicClient } from '@lybic/core'

const lybic = new LybicClient({
baseUrl: 'https://api.lybic.cn',
orgId: 'ORG-xxxx',
apiKey: 'lysk-your-api-key-here',
})

// Prepare the script content
const scriptContent = `Write-Host "Starting model training..."
for ($i = 1; $i -le 100; $i++) {
Start-Sleep -Seconds 1
Write-Host "Progress: $i/100"
}
Write-Host "Training complete!"
`

// Base64 encode
const stdinBase64 = btoa(scriptContent)

// Write to file
const result = await lybic.execSandboxProcess('SBX-xxxx', {
executable: 'powershell.exe',
args: ['-Command', '$input | Out-File -FilePath C:\\Users\\Administrator\\scripts\\train.ps1 -Encoding UTF8'],
stdinBase64: stdinBase64,
})

if (result.data?.exitCode === 0) {
console.log('Script created successfully')
} else {
console.log('Create failed:', atob(result.data?.stderrBase64 || ''))
}
package main

import (
"context"
"encoding/base64"
"fmt"
"github.com/lybic/lybic-sdk-go"
)

func main() {
ctx := context.Background()
client, _ := lybic.NewClient(nil)

// Prepare the script content
scriptContent := `Write-Host "Starting model training..."
for ($i = 1; $i -le 100; $i++) {
Start-Sleep -Seconds 1
Write-Host "Progress: $i/100"
}
Write-Host "Training complete!"
`

// Base64 encode
stdinBase64 := base64.StdEncoding.EncodeToString([]byte(scriptContent))

// Write to file
cmd := "$input | Out-File -FilePath C:\\Users\\Administrator\\scripts\\train.ps1 -Encoding UTF8"
processDto := lybic.SandboxProcessRequestDto{
Executable:  "powershell.exe",
Args:        []string{"-Command", cmd},
StdinBase64: &stdinBase64,
}

result, _ := client.ExecSandboxProcess(ctx, "SBX-xxxx", processDto)

if result.ExitCode == 0 {
fmt.Println("Script created successfully")
} else {
stderr, _ := base64.StdEncoding.DecodeString(result.StderrBase64)
fmt.Printf("Create failed: %s\n", string(stderr))
}
}

Step 2: Start the background task with Start-Job

Use PowerShell's Start-Job to start a background task. The command returns immediately while the task continues running in the background.

# Start the background task
curl -X POST "https://api.lybic.cn/api/orgs/{orgId}/sandboxes/{sandboxId}/process" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"executable": "powershell.exe",
"args": [
"-Command",
"Start-Job -Name MyTrainingTask -FilePath C:\\Users\\Administrator\\scripts\\train.ps1"
]
}'
import asyncio
import base64
from lybic import LybicClient, LybicAuth

async def main():
async with LybicClient(
LybicAuth(
org_id="ORG-xxxx",
api_key="lysk-xxxxxxxxxxx",
endpoint="https://api.lybic.cn/"
)
) as client:
# Start the background task
result = await client.sandbox.execute_process(
"SBX-xxxx",
executable="powershell.exe",
args=[
"-Command",
"Start-Job -Name MyTrainingTask -FilePath C:\\Users\\Administrator\\scripts\\train.ps1"
]
)

output = base64.b64decode(result.stdoutBase64 or '').decode()
print(f"Task started: {output}")
print(f"Exit code: {result.exitCode}")

if __name__ == "__main__":
asyncio.run(main())
import { LybicClient } from '@lybic/core'

const lybic = new LybicClient({
baseUrl: 'https://api.lybic.cn',
orgId: 'ORG-xxxx',
apiKey: 'lysk-your-api-key-here',
})

// Start the background task
const result = await lybic.execSandboxProcess('SBX-xxxx', {
executable: 'powershell.exe',
args: [
'-Command',
'Start-Job -Name MyTrainingTask -FilePath C:\\Users\\Administrator\\scripts\\train.ps1',
],
})

const output = atob(result.data?.stdoutBase64 || '')
console.log('Task started:', output)
console.log('Exit code:', result.data?.exitCode)
package main

import (
"context"
"encoding/base64"
"fmt"
"github.com/lybic/lybic-sdk-go"
)

func main() {
ctx := context.Background()
client, _ := lybic.NewClient(nil)

// Start the background task
processDto := lybic.SandboxProcessRequestDto{
Executable: "powershell.exe",
Args: []string{
"-Command",
"Start-Job -Name MyTrainingTask -FilePath C:\\Users\\Administrator\\scripts\\train.ps1",
},
}

result, err := client.ExecSandboxProcess(ctx, "SBX-xxxx", processDto)
if err != nil {
fmt.Println("Error starting task:", err)
return
}

stdout, _ := base64.StdEncoding.DecodeString(result.StdoutBase64)
fmt.Printf("Task started: %s\n", string(stdout))
fmt.Printf("Exit code: %d\n", result.ExitCode)
}

Important parameter notes:

  • -Name <name>: set the task name (used later to query status/output)
  • -FilePath <path>: path to the script file to execute
  • -ScriptBlock { ... }: alternatively provide a script block (instead of -FilePath)

Example output:

Id     Name               PSJobTypeName   State         HasMoreData     Location             Command
--     ----               -------------   -----         -----------     --------             -------
1      MyTrainingTask     BackgroundJob   Running       True            localhost            C:\Users\Administrator\scripts\train.ps1

Step 3: Check task status

3.1 List all background tasks

Use Get-Job to list all background tasks:

# List all tasks
curl -X POST "https://api.lybic.cn/api/orgs/{orgId}/sandboxes/{sandboxId}/process" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"executable": "powershell.exe",
"args": ["-Command", "Get-Job | Format-Table -AutoSize"]
}'
import asyncio
import base64
from lybic import LybicClient, LybicAuth

async def main():
async with LybicClient(
LybicAuth(
org_id="ORG-xxxx",
api_key="lysk-xxxxxxxxxxx",
endpoint="https://api.lybic.cn/"
)
) as client:
# List all tasks
result = await client.sandbox.execute_process(
"SBX-xxxx",
executable="powershell.exe",
args=["-Command", "Get-Job | Format-Table -AutoSize"]
)

jobs = base64.b64decode(result.stdoutBase64 or '').decode()
print('All tasks:')
print(jobs)

if __name__ == "__main__":
asyncio.run(main())
import { LybicClient } from '@lybic/core'

const lybic = new LybicClient({
baseUrl: 'https://api.lybic.cn',
orgId: 'ORG-xxxx',
apiKey: 'lysk-your-api-key-here',
})

// List all tasks
const result = await lybic.execSandboxProcess('SBX-xxxx', {
executable: 'powershell.exe',
args: ['-Command', 'Get-Job | Format-Table -AutoSize'],
})

const jobs = atob(result.data?.stdoutBase64 || '')
console.log('All tasks:')
console.log(jobs)
package main

import (
"context"
"encoding/base64"
"fmt"
"github.com/lybic/lybic-sdk-go"
)

func main() {
ctx := context.Background()
client, _ := lybic.NewClient(nil)

// List all tasks
processDto := lybic.SandboxProcessRequestDto{
Executable: "powershell.exe",
Args:       []string{"-Command", "Get-Job | Format-Table -AutoSize"},
}

result, _ := client.ExecSandboxProcess(ctx, "SBX-xxxx", processDto)

stdout, _ := base64.StdEncoding.DecodeString(result.StdoutBase64)
fmt.Println('All tasks:')
fmt.Println(string(stdout))
}

3.2 Check a specific task

Use Get-Job -Name to view detailed status for a specific task:

# Check a specific task
curl -X POST "https://api.lybic.cn/api/orgs/{orgId}/sandboxes/{sandboxId}/process" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"executable": "powershell.exe",
"args": ["-Command", "Get-Job -Name MyTrainingTask | Format-List"]
}'
import asyncio
import base64
from lybic import LybicClient, LybicAuth

async def main():
async with LybicClient(
LybicAuth(
org_id="ORG-xxxx",
api_key="lysk-xxxxxxxxxxx",
endpoint="https://api.lybic.cn/"
)
) as client:
# Check task status
result = await client.sandbox.execute_process(
"SBX-xxxx",
executable="powershell.exe",
args=["-Command", "Get-Job -Name MyTrainingTask | Format-List"]
)

status = base64.b64decode(result.stdoutBase64 or '').decode()
print('Task status:')
print(status)

if __name__ == "__main__":
asyncio.run(main())
import { LybicClient } from '@lybic/core'

const lybic = new LybicClient({
baseUrl: 'https://api.lybic.cn',
orgId: 'ORG-xxxx',
apiKey: 'lysk-your-api-key-here',
})

// Check task status
const result = await lybic.execSandboxProcess('SBX-xxxx', {
executable: 'powershell.exe',
args: ['-Command', 'Get-Job -Name MyTrainingTask | Format-List'],
})

const status = atob(result.data?.stdoutBase64 || '')
console.log('Task status:')
console.log(status)
package main

import (
"context"
"encoding/base64"
"fmt"
"github.com/lybic/lybic-sdk-go"
)

func main() {
ctx := context.Background()
client, _ := lybic.NewClient(nil)

// Check task status
processDto := lybic.SandboxProcessRequestDto{
Executable: "powershell.exe",
Args:       []string{"-Command", "Get-Job -Name MyTrainingTask | Format-List"},
}

result, _ := client.ExecSandboxProcess(ctx, "SBX-xxxx", processDto)

stdout, _ := base64.StdEncoding.DecodeString(result.StdoutBase64)
fmt.Println('Task status:')
fmt.Println(string(stdout))
}

Example status output:

Id         : 1
Name       : MyTrainingTask
PSJobTypeName : BackgroundJob
State      : Running
HasMoreData: True
Location   : localhost
Command    : C:\Users\Administrator\scripts\train.ps1

Task state notes:

  • Running: task is running
  • Completed: task completed
  • Failed: task failed
  • Stopped: task was stopped

3.3 Get task output

Use Receive-Job to retrieve task output:

# Get task output (do not clear output)
curl -X POST "https://api.lybic.cn/api/orgs/{orgId}/sandboxes/{sandboxId}/process" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"executable": "powershell.exe",
"args": ["-Command", "Receive-Job -Name MyTrainingTask -Keep"]
}'
import asyncio
import base64
from lybic import LybicClient, LybicAuth

async def main():
async with LybicClient(
LybicAuth(
org_id="ORG-xxxx",
api_key="lysk-xxxxxxxxxxx",
endpoint="https://api.lybic.cn/"
)
) as client:
# Get task output
result = await client.sandbox.execute_process(
"SBX-xxxx",
executable="powershell.exe",
args=["-Command", "Receive-Job -Name MyTrainingTask -Keep"]
)

output = base64.b64decode(result.stdoutBase64 or '').decode()
print('Task output:')
print(output)

if __name__ == "__main__":
asyncio.run(main())
import { LybicClient } from '@lybic/core'

const lybic = new LybicClient({
baseUrl: 'https://api.lybic.cn',
orgId: 'ORG-xxxx',
apiKey: 'lysk-your-api-key-here',
})

// Get task output
const result = await lybic.execSandboxProcess('SBX-xxxx', {
executable: 'powershell.exe',
args: ['-Command', 'Receive-Job -Name MyTrainingTask -Keep'],
})

const output = atob(result.data?.stdoutBase64 || '')
console.log('Task output:')
console.log(output)
package main

import (
"context"
"encoding/base64"
"fmt"
"github.com/lybic/lybic-sdk-go"
)

func main() {
ctx := context.Background()
client, _ := lybic.NewClient(nil)

// Get task output
processDto := lybic.SandboxProcessRequestDto{
Executable: "powershell.exe",
Args:       []string{"-Command", "Receive-Job -Name MyTrainingTask -Keep"},
}

result, _ := client.ExecSandboxProcess(ctx, "SBX-xxxx", processDto)

stdout, _ := base64.StdEncoding.DecodeString(result.StdoutBase64)
fmt.Println('Task output:')
fmt.Println(string(stdout))
}

Important parameters:

  • -Keep: keep output so you can fetch it again later (without it, output is cleared after reading)
  • -Wait: wait for task completion (not recommended; blocks until the task ends)

3.4 Wait for completion and fetch results

Use Wait-Job and Receive-Job to wait for the job to finish:

import asyncio
import base64
from lybic import LybicClient, LybicAuth

async def wait_for_job(client, sandbox_id: str, job_name: str):
"""Wait for the task to complete and fetch the result"""
while True:
# Check task state
result = await client.sandbox.execute_process(
sandbox_id,
executable="powershell.exe",
args=["-Command", f"(Get-Job -Name {job_name}).State"]
)

state = base64.b64decode(result.stdoutBase64 or '').decode().strip()
print(f"Task state: {state}")

if state in ["Completed", "Failed", "Stopped"]:
# Get task output
result = await client.sandbox.execute_process(
sandbox_id,
executable="powershell.exe",
args=["-Command", f"Receive-Job -Name {job_name}"]
)

output = base64.b64decode(result.stdoutBase64 or '').decode()
return state, output

# Wait 5 seconds and check again
await asyncio.sleep(5)

async def main():
async with LybicClient(
LybicAuth(
org_id="ORG-xxxx",
api_key="lysk-xxxxxxxxxxx",
endpoint="https://api.lybic.cn/"
)
) as client:
state, output = await wait_for_job(client, "SBX-xxxx", "MyTrainingTask")
print(f"\nTask finished, state: {state}")
print(f"Output:\n{output}")

if __name__ == "__main__":
asyncio.run(main())
import { LybicClient } from '@lybic/core'

const lybic = new LybicClient({
baseUrl: 'https://api.lybic.cn',
orgId: 'ORG-xxxx',
apiKey: 'lysk-your-api-key-here',
})

async function waitForJob(sandboxId: string, jobName: string) {
while (true) {
// Check task state
const statusResult = await lybic.execSandboxProcess(sandboxId, {
executable: 'powershell.exe',
args: ['-Command', `(Get-Job -Name ${jobName}).State`],
})

const state = atob(statusResult.data?.stdoutBase64 || '').trim()
console.log('Task state:', state)

if (['Completed', 'Failed', 'Stopped'].includes(state)) {
// Get task output
const outputResult = await lybic.execSandboxProcess(sandboxId, {
executable: 'powershell.exe',
args: ['-Command', `Receive-Job -Name ${jobName}`],
})

const output = atob(outputResult.data?.stdoutBase64 || '')
return { state, output }
}

// Wait 5 seconds and check again
await new Promise(resolve => setTimeout(resolve, 5000))
}
}

const { state, output } = await waitForJob('SBX-xxxx', 'MyTrainingTask')
console.log('\nTask finished, state:', state)
console.log('Output:\n', output)

Scheduled Tasks are the most reliable option for long-running or persistent tasks. Tasks can persist even after the sandbox restarts (not destroyed).

Create a scheduled task

Use schtasks to create a scheduled task that starts immediately:

# Create and start a scheduled task
curl -X POST "https://api.lybic.cn/api/orgs/{orgId}/sandboxes/{sandboxId}/process" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"executable": "schtasks",
"args": [
"/Create",
"/TN", "MyTrainingTask",
"/TR", "powershell.exe -File C:\\Users\\Administrator\\scripts\\train.ps1",
"/SC", "ONCE",
"/ST", "00:00",
"/F"
]
}'
import asyncio
import base64
from lybic import LybicClient, LybicAuth

async def main():
async with LybicClient(
LybicAuth(
org_id="ORG-xxxx",
api_key="lysk-xxxxxxxxxxx",
endpoint="https://api.lybic.cn/"
)
) as client:
# Create the scheduled task
result = await client.sandbox.execute_process(
"SBX-xxxx",
executable="schtasks",
args=[
"/Create",
"/TN", "MyTrainingTask",
"/TR", "powershell.exe -File C:\\Users\\Administrator\\scripts\\train.ps1",
"/SC", "ONCE",
"/ST", "00:00",
"/F"
]
)

output = base64.b64decode(result.stdoutBase64 or '').decode()
print(f"Scheduled task creation result: {output}")

# Start the task immediately
result = await client.sandbox.execute_process(
"SBX-xxxx",
executable="schtasks",
args=["/Run", "/TN", "MyTrainingTask"]
)

output = base64.b64decode(result.stdoutBase64 or '').decode()
print(f"Task start result: {output}")

if __name__ == "__main__":
asyncio.run(main())
import { LybicClient } from '@lybic/core'

const lybic = new LybicClient({
baseUrl: 'https://api.lybic.cn',
orgId: 'ORG-xxxx',
apiKey: 'lysk-your-api-key-here',
})

// Create the scheduled task
let result = await lybic.execSandboxProcess('SBX-xxxx', {
executable: 'schtasks',
args: [
'/Create',
'/TN', 'MyTrainingTask',
'/TR', 'powershell.exe -File C:\\Users\\Administrator\\scripts\\train.ps1',
'/SC', 'ONCE',
'/ST', '00:00',
'/F',
],
})

console.log('Scheduled task creation result:', atob(result.data?.stdoutBase64 || ''))

// Start the task immediately
result = await lybic.execSandboxProcess('SBX-xxxx', {
executable: 'schtasks',
args: ['/Run', '/TN', 'MyTrainingTask'],
})

console.log('Task start result:', atob(result.data?.stdoutBase64 || ''))
package main

import (
"context"
"encoding/base64"
"fmt"
"github.com/lybic/lybic-sdk-go"
)

func main() {
ctx := context.Background()
client, _ := lybic.NewClient(nil)

// Create the scheduled task
processDto := lybic.SandboxProcessRequestDto{
Executable: "schtasks",
Args: []string{
"/Create",
"/TN", "MyTrainingTask",
"/TR", "powershell.exe -File C:\\Users\\Administrator\\scripts\\train.ps1",
"/SC", "ONCE",
"/ST", "00:00",
"/F",
},
}

result, _ := client.ExecSandboxProcess(ctx, "SBX-xxxx", processDto)
stdout, _ := base64.StdEncoding.DecodeString(result.StdoutBase64)
fmt.Printf("Scheduled task creation result: %s\n", string(stdout))

// Start the task immediately
processDto = lybic.SandboxProcessRequestDto{
Executable: "schtasks",
Args:       []string{"/Run", "/TN", "MyTrainingTask"},
}

result, _ = client.ExecSandboxProcess(ctx, "SBX-xxxx", processDto)
stdout, _ = base64.StdEncoding.DecodeString(result.StdoutBase64)
fmt.Printf("Task start result: %s\n", string(stdout))
}

Important parameter notes:

  • /Create: create a new task
  • /TN <taskName>: task name
  • /TR <command>: command to run
  • /SC ONCE: run once (also supports DAILY, WEEKLY, etc.)
  • /ST <time>: scheduled start time (format: HH:MM)
  • /F: force create; overwrite if the task already exists
  • /Run: run immediately (do not wait for scheduled time)

Check scheduled task status

# Query task status
curl -X POST "https://api.lybic.cn/api/orgs/{orgId}/sandboxes/{sandboxId}/process" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"executable": "schtasks",
"args": ["/Query", "/TN", "MyTrainingTask", "/V", "/FO", "LIST"]
}'
import asyncio
import base64
from lybic import LybicClient, LybicAuth

async def main():
async with LybicClient(
LybicAuth(
org_id="ORG-xxxx",
api_key="lysk-xxxxxxxxxxx",
endpoint="https://api.lybic.cn/"
)
) as client:
# Query task status
result = await client.sandbox.execute_process(
"SBX-xxxx",
executable="schtasks",
args=["/Query", "/TN", "MyTrainingTask", "/V", "/FO", "LIST"]
)

status = base64.b64decode(result.stdoutBase64 or '').decode()
print('Task status:')
print(status)

if __name__ == "__main__":
asyncio.run(main())
import { LybicClient } from '@lybic/core'

const lybic = new LybicClient({
baseUrl: 'https://api.lybic.cn',
orgId: 'ORG-xxxx',
apiKey: 'lysk-your-api-key-here',
})

// Query task status
const result = await lybic.execSandboxProcess('SBX-xxxx', {
executable: 'schtasks',
args: ['/Query', '/TN', 'MyTrainingTask', '/V', '/FO', 'LIST'],
})

const status = atob(result.data?.stdoutBase64 || '')
console.log('Task status:')
console.log(status)
package main

import (
"context"
"encoding/base64"
"fmt"
"github.com/lybic/lybic-sdk-go"
)

func main() {
ctx := context.Background()
client, _ := lybic.NewClient(nil)

// Query task status
processDto := lybic.SandboxProcessRequestDto{
Executable: "schtasks",
Args:       []string{"/Query", "/TN", "MyTrainingTask", "/V", "/FO", "LIST"},
}

result, _ := client.ExecSandboxProcess(ctx, "SBX-xxxx", processDto)

stdout, _ := base64.StdEncoding.DecodeString(result.StdoutBase64)
fmt.Println('Task status:')
fmt.Println(string(stdout))
}

Parameter notes:

  • /Query: query a task
  • /V: verbose output
  • /FO LIST: format as a list

Task state notes:

  • Ready: ready (not running)
  • Running: running
  • Disabled: disabled

Redirect output to a file

Since scheduled task output is not as easy to retrieve as PowerShell Jobs, it is recommended to redirect output to a file:

import asyncio
import base64
from lybic import LybicClient, LybicAuth

async def main():
async with LybicClient(
LybicAuth(
org_id="ORG-xxxx",
api_key="lysk-xxxxxxxxxxx",
endpoint="https://api.lybic.cn/"
)
) as client:
# Create a scheduled task with output redirection
result = await client.sandbox.execute_process(
"SBX-xxxx",
executable="schtasks",
args=[
"/Create",
"/TN", "MyTrainingTask",
"/TR", "powershell.exe -File C:\\Users\\Administrator\\scripts\\train.ps1 > C:\\Users\\Administrator\\logs\\output.log 2>&1",
"/SC", "ONCE",
"/ST", "00:00",
"/F"
]
)

print('Task created successfully')

# Start the task
await client.sandbox.execute_process(
"SBX-xxxx",
executable="schtasks",
args=["/Run", "/TN", "MyTrainingTask"]
)

print('Task started')

# Wait a bit before reading the log
await asyncio.sleep(10)

# Read the output log
result = await client.sandbox.execute_process(
"SBX-xxxx",
executable="powershell.exe",
args=["-Command", "Get-Content C:\\Users\\Administrator\\logs\\output.log"]
)

output = base64.b64decode(result.stdoutBase64 or '').decode()
print('Task output:')
print(output)

if __name__ == "__main__":
asyncio.run(main())
import { LybicClient } from '@lybic/core'

const lybic = new LybicClient({
baseUrl: 'https://api.lybic.cn',
orgId: 'ORG-xxxx',
apiKey: 'lysk-your-api-key-here',
})

// Create a scheduled task with output redirection
await lybic.execSandboxProcess('SBX-xxxx', {
executable: 'schtasks',
args: [
'/Create',
'/TN', 'MyTrainingTask',
'/TR', 'powershell.exe -File C:\\Users\\Administrator\\scripts\\train.ps1 > C:\\Users\\Administrator\\logs\\output.log 2>&1',
'/SC', 'ONCE',
'/ST', '00:00',
'/F',
],
})

console.log('Task created successfully')

// Start the task
await lybic.execSandboxProcess('SBX-xxxx', {
executable: 'schtasks',
args: ['/Run', '/TN', 'MyTrainingTask'],
})

console.log('Task started')

// Wait a bit before reading the log
await new Promise(resolve => setTimeout(resolve, 10000))

// Read the output log
const result = await lybic.execSandboxProcess('SBX-xxxx', {
executable: 'powershell.exe',
args: ['-Command', 'Get-Content C:\\Users\\Administrator\\logs\\output.log'],
})

const output = atob(result.data?.stdoutBase64 || '')
console.log('Task output:')
console.log(output)

Full example: Run a Python web server

Below is a complete example showing how to run a long-running Python web server in a Windows sandbox:

import asyncio
import base64
from lybic import LybicClient, LybicAuth

async def run_web_server():
async with LybicClient(
LybicAuth(
org_id="ORG-xxxx",
api_key="lysk-xxxxxxxxxxx",
endpoint="https://api.lybic.cn/"
)
) as client:
sandbox_id = "SBX-xxxx"

# 1. Create the web server script
server_code = """
from http.server import HTTPServer, BaseHTTPRequestHandler
import json

class Handler(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-Type', 'application/json')
self.end_headers()
response = {'status': 'ok', 'message': 'Hello from Lybic Windows!'}
self.wfile.write(json.dumps(response).encode())

def log_message(self, format, *args):
print(f"{self.address_string()} - {format % args}")

if __name__ == '__main__':
server = HTTPServer(('0.0.0.0', 8080), Handler)
print("Server started at http://0.0.0.0:8080")
server.serve_forever()
"""

stdin_data = base64.b64encode(server_code.encode('utf-8')).decode()

# Create directory and script file
result = await client.sandbox.execute_process(
sandbox_id,
executable="powershell.exe",
args=["-Command", "New-Item -ItemType Directory -Force -Path C:\\Users\\Administrator\\server; $input | Out-File -FilePath C:\\Users\\Administrator\\server\\app.py -Encoding UTF8"],
stdinBase64=stdin_data
)

if result.exitCode != 0:
print('Failed to create server script')
return

print('✓ Server script created successfully')

# 2. Start the server with Start-Job
result = await client.sandbox.execute_process(
sandbox_id,
executable="powershell.exe",
args=[
"-Command",
"Start-Job -Name WebServer -ScriptBlock { python C:\\Users\\Administrator\\server\\app.py }"
]
)

output = base64.b64decode(result.stdoutBase64 or '').decode()
print(f"✓ Web server started: {output}")

# 3. Wait a few seconds for the server to start
await asyncio.sleep(3)

# 4. Check server status
result = await client.sandbox.execute_process(
sandbox_id,
executable="powershell.exe",
args=["-Command", "(Get-Job -Name WebServer).State"]
)

status = base64.b64decode(result.stdoutBase64 or '').decode().strip()
print(f"✓ Server status: {status}")

# 5. Read task output
result = await client.sandbox.execute_process(
sandbox_id,
executable="powershell.exe",
args=["-Command", "Receive-Job -Name WebServer -Keep"]
)

logs = base64.b64decode(result.stdoutBase64 or '').decode()
print('✓ Server logs:')
print(logs)

# 6. Test the server
result = await client.sandbox.execute_process(
sandbox_id,
executable="powershell.exe",
args=["-Command", "Invoke-WebRequest -Uri http://localhost:8080 -UseBasicParsing | Select-Object -ExpandProperty Content"]
)

response = base64.b64decode(result.stdoutBase64 or '').decode()
print(f"✓ Server response: {response}")

if __name__ == "__main__":
asyncio.run(run_web_server())
import { LybicClient } from '@lybic/core'

const lybic = new LybicClient({
baseUrl: 'https://api.lybic.cn',
orgId: 'ORG-xxxx',
apiKey: 'lysk-your-api-key-here',
})

async function runWebServer() {
const sandboxId = 'SBX-xxxx'

// 1. Create the web server script
const serverCode = `
from http.server import HTTPServer, BaseHTTPRequestHandler
import json

class Handler(BaseHTTPRequestHandler):
def do_GET(self):
    self.send_response(200)
    self.send_header('Content-Type', 'application/json')
    self.end_headers()
    response = {'status': 'ok', 'message': 'Hello from Lybic Windows!'}
    self.wfile.write(json.dumps(response).encode())

def log_message(self, format, *args):
    print(f"{self.address_string()} - {format % args}")

if __name__ == '__main__':
    server = HTTPServer(('0.0.0.0', 8080), Handler)
    print("Server started at http://0.0.0.0:8080")
    server.serve_forever()
`

const stdinBase64 = btoa(serverCode)

// Create directory and script file
let result = await lybic.execSandboxProcess(sandboxId, {
executable: 'powershell.exe',
args: ['-Command', 'New-Item -ItemType Directory -Force -Path C:\\Users\\Administrator\\server; $input | Out-File -FilePath C:\\Users\\Administrator\\server\\app.py -Encoding UTF8'],
stdinBase64: stdinBase64,
})

if (result.data?.exitCode !== 0) {
console.log('Failed to create server script')
return
}

console.log('✓ Server script created successfully')

// 2. Start the server with Start-Job
result = await lybic.execSandboxProcess(sandboxId, {
executable: 'powershell.exe',
args: [
'-Command',
'Start-Job -Name WebServer -ScriptBlock { python C:\\Users\\Administrator\\server\\app.py }',
],
})

const output = atob(result.data?.stdoutBase64 || '')
console.log('✓ Web server started:', output)

// 3. Wait a few seconds for the server to start
await new Promise(resolve => setTimeout(resolve, 3000))

// 4. Check server status
result = await lybic.execSandboxProcess(sandboxId, {
executable: 'powershell.exe',
args: ['-Command', '(Get-Job -Name WebServer).State'],
})

const status = atob(result.data?.stdoutBase64 || '').trim()
console.log('✓ Server status:', status)

// 5. Read task output
result = await lybic.execSandboxProcess(sandboxId, {
executable: 'powershell.exe',
args: ['-Command', 'Receive-Job -Name WebServer -Keep'],
})

const logs = atob(result.data?.stdoutBase64 || '')
console.log('✓ Server logs:')
console.log(logs)

// 6. Test the server
result = await lybic.execSandboxProcess(sandboxId, {
executable: 'powershell.exe',
args: ['-Command', 'Invoke-WebRequest -Uri http://localhost:8080 -UseBasicParsing | Select-Object -ExpandProperty Content'],
})

const response = atob(result.data?.stdoutBase64 || '')
console.log('✓ Server response:', response)
}

runWebServer()

Advanced usage

Stop a PowerShell Job

# Stop a specific job
Stop-Job -Name MyTrainingTask

# Remove a job
Remove-Job -Name MyTrainingTask

Stop a scheduled task

# Stop the task
schtasks /End /TN MyTrainingTask

# Delete the task
schtasks /Delete /TN MyTrainingTask /F

List all tasks

# List all PowerShell Jobs
Get-Job

# List all scheduled tasks
schtasks /Query /FO LIST

Use Start-Process to launch a background process

For simpler scenarios, you can use Start-Process:

Start-Process -FilePath "python" -ArgumentList "C:\Users\Administrator\scripts\train.py" -WindowStyle Hidden -RedirectStandardOutput "C:\Users\Administrator\logs\output.log"

Note: processes started with Start-Process are harder to track and manage; prefer PowerShell Jobs or Scheduled Tasks.

Method comparison

FeaturePowerShell JobsScheduled TasksStart-Process
Ease of use⭐⭐⭐⭐⭐⭐⭐⭐
Status query⭐⭐⭐⭐⭐
Output retrieval⭐⭐⭐
Persistence
Fit for long-running tasks⭐⭐⭐⭐⭐⭐⭐
Resource isolation⭐⭐⭐⭐⭐⭐⭐

Recommended scenarios:

  • PowerShell Jobs: short-to-medium tasks (minutes to hours) where you want easy output retrieval
  • Scheduled Tasks: long-running tasks; needs persistence or to continue after sandbox restart
  • Start-Process: simple background launches where monitoring requirements are low

Best practices

  1. Choose the right method: pick PowerShell Jobs or Scheduled Tasks based on runtime and monitoring needs
  2. Use meaningful task names: makes later querying and management easier
  3. Redirect output: for Scheduled Tasks, redirect output to a file so you can review it
  4. Check status regularly: query status periodically to catch issues early
  5. Error handling: add try/catch blocks and logging in your scripts
  6. Clean up tasks: delete tasks you no longer need to free resources
  7. Use absolute paths: use full file paths in commands to avoid path issues

FAQ

Q: What's the difference between a PowerShell Job and a Scheduled Task?

PowerShell Jobs are lighter-weight and good for temporary tasks; they may be lost when the PowerShell session ends. Scheduled Tasks are persistent and better for long-running tasks.

Q: How do I see detailed error output?

For PowerShell Jobs, use Receive-Job -Name <taskName> -Keep to retrieve all output (including errors). For Scheduled Tasks, redirect output to a file.

Q: Will the task keep running after the sandbox restarts?

  • PowerShell Jobs: no; the job is lost when the session ends
  • Scheduled Tasks: yes; scheduled tasks are persistent

Q: Can I run multiple tasks at the same time?

Yes. Just use a different name for each task.

Q: How do I limit resource usage?

Scheduled Tasks can limit CPU and memory via task settings. PowerShell Jobs have weaker resource management.

Q: How do I monitor task output in real time?

Due to API timeout limits, poll status/output periodically rather than monitoring in real time. For example, call Receive-Job -Keep every few seconds or read a redirected log file.


On this page