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:
- PowerShell Jobs (
Start-Job): simplest; good for short-to-medium background tasks - Scheduled Tasks (
schtasks): most reliable; good for long-running and persistent tasks - Start-Process: good for simple background process launches, but weaker monitoring
This document focuses on the first two approaches.
Method 1: PowerShell Jobs (recommended for short-to-medium tasks)
PowerShell Jobs are the simplest and most direct option, suitable for tasks that run from minutes to hours.
Step overview
- Prepare a script or program: transfer the script/program to the sandbox
- Start the task: launch it in the background with
Start-Job - 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.ps1Step 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.ps1Task state notes:
Running: task is runningCompleted: task completedFailed: task failedStopped: 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)Method 2: Scheduled Tasks (recommended for long-running tasks)
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: runningDisabled: 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 MyTrainingTaskStop a scheduled task
# Stop the task
schtasks /End /TN MyTrainingTask
# Delete the task
schtasks /Delete /TN MyTrainingTask /FList all tasks
# List all PowerShell Jobs
Get-Job
# List all scheduled tasks
schtasks /Query /FO LISTUse 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
| Feature | PowerShell Jobs | Scheduled Tasks | Start-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
- Choose the right method: pick PowerShell Jobs or Scheduled Tasks based on runtime and monitoring needs
- Use meaningful task names: makes later querying and management easier
- Redirect output: for Scheduled Tasks, redirect output to a file so you can review it
- Check status regularly: query status periodically to catch issues early
- Error handling: add try/catch blocks and logging in your scripts
- Clean up tasks: delete tasks you no longer need to free resources
- 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.
Related docs
- Run commands and execute code - short-lived command execution
- File transfer - upload scripts and download results
- Python SDK usage guide
- TypeScript SDK usage guide