skills/lumina-app-proxy-register/SKILL.md
Register and verify a sandbox-hosted HTTP service through Lumina App Proxy (LuminaProxyAPI). Use when the user wants to spin up a tiny FastAPI server inside a Lumina sandbox, expose it under {appId}.{BaseDomain}, hand the resulting URL to teammates, or compare access_scope=owner vs access_scope=all behavior of the AppProxyAuthHandler. Pairs with lumina-eps-token to acquire the bearer token and create the sandbox first.
npx skillsauth add liulixiang1988/agent-skills lumina-app-proxy-registerInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
End-to-end workflow that takes a live Lumina sandbox and a bearer token, drops a minimal FastAPI server into the sandbox, registers it via egress-llm's /app/register/agent, prints the public {appId}.{BaseDomain} URL, and verifies the round-trip through LuminaProxyAPI with one S2S call.
AppProxyAuthHandler (AccessScope = owner vs all) — for example to confirm a teammate's token is rejected on an owner-scoped URL.AppProxyAuthStateCookie) end-to-end: the registered URL is the ideal target to hit in a fresh browser to trigger the AAD redirect.app_url from LuminaProxyAPI.This skill needs a ComputerId and a bearer token. The easiest way to obtain both is the sibling lumina-eps-token skill:
lumina-eps-token to acquire the token (cached at sources/dev/SandboxService/AIAgents/ts-agents/skills-agent/scripts/test_sessions/auth_token.txt) and initialize a sandbox (it prints Computer ID: skills-agent-<uuid>).ComputerId and the cached token to this skill.| Param | Type | Description |
|---|---|---|
| ComputerId / --computer-id | string | The sandbox ID returned by eps_client.py (looks like skills-agent-<uuid> or playground-<uuid>). |
| AuthToken / --auth-token | string | OBO/PFT bearer token. For owner-scoped apps the token's oid claim must equal the sandbox owner's oid. |
| Param | Default | Notes |
|---|---|---|
| LuminaApiBase / --lumina-api-base | https://luminaserviceapi-b-4.luminadevaks-westus3.dev.copilotlumina.com | Pick a different ring/region if needed. |
| FeatureKind / --feature-kind | terminal | One of agent, desktop, operator, terminal (validated by RegisterAppService._supportedFeatureKinds). |
| FeatureName / --feature-name | terminal-shell | Logical feature/service name. |
| ServerPort / --server-port | 19101 | Must fall inside the sandbox image's APP_HOSTING_PORT_RANGE. |
| AccessScope / --access-scope | owner | owner ⇒ requester only; all ⇒ any authenticated user. |
| -SkipS2STest / --skip-s2s | off | Skip Step 5 if you only need the URL printed. |
Same convention as lumina-eps-token. The path varies per host install:
CLAUDE_SKILL_DIR, SKILL_DIR, AGENT_SKILL_PATH, or AGENTS_SKILL_DIR, use it.skills/lumina-app-proxy-register relative to the workspace root.lumina-app-proxy-register directory whose scripts/ contains both helpers:
$HOME/.agents/skills/lumina-app-proxy-register$HOME/.claude/skills/lumina-app-proxy-register$HOME/.codex/skills/lumina-app-proxy-register$HOME/.gemini/skills/lumina-app-proxy-register$HOME/.copilot/skills/lumina-app-proxy-register**/skills/lumina-app-proxy-register/SKILL.md reachable from cwd or the project worktree.On Windows, $HOME is $env:USERPROFILE.
& "$skillDir\scripts\Invoke-LuminaAppProxyRegister.ps1" `
-ComputerId "skills-agent-<uuid>" `
-AuthToken (Get-Content "<repo>\sources\dev\SandboxService\AIAgents\ts-agents\skills-agent\scripts\test_sessions\auth_token.txt" -Raw).Trim() `
-AccessScope "owner"
TOKEN=$(cat "<repo>/sources/dev/SandboxService/AIAgents/ts-agents/skills-agent/scripts/test_sessions/auth_token.txt")
bash "$skillDir/scripts/invoke-lumina-app-proxy-register.sh" \
--computer-id "skills-agent-<uuid>" \
--auth-token "$TOKEN" \
--access-scope owner
server.py to /home/oai/share/server.py via POST /api/agent/storage/file/batchUpload. The payload is a minimal FastAPI app with GET / returning {"status":"ok"} and GET /health returning {"healthy":true}.server-session via POST /api/agent/container/exec (sessionDuration=2400), then feedChars runs nohup python /home/oai/share/server.py <port> &.curl -X POST ${EGRESS_LLM_API_ENDPOINT}/app/register/agent from inside the sandbox with body {"feature_kind", "feature_name", "port", "path": "/", "access_scope"}. The egress-llm forwards to LuminaProxyAPI's RegisterAppService.RegisterWebAppAsync, which assigns a fresh appId (Guid) and returns {registered_app_id, app_url}.https://<appId>.luminaproxyapi-<ring>-<region>.<ring>.copilotlumina.com/.Invoke-WebRequest/curl the URL with Authorization: Bearer <AuthToken>; expect 200 {"status":"ok"} when the auth+proxy pipeline is healthy.AppProxyAuthHandler.AuthorizeSandboxAccessAsync only enforces the owner check when appContent.AccessScope == "owner" (constant-time string comparison ignores case). Any other value — including the empty string — falls through to a 200.
| AccessScope on the registration | Same user (token oid == sandbox.ObjectId) | Different user |
|---|---|---|
| owner | 200 | 401 Unauthorized |
| all | 200 | 200 |
| (unset on request) | falls back to _appProxyOptions.Value.AccessScope — dev/test rings default to "all", prod/msit/sdf default to "owner". |
Confusing observation: in dev/test, registering an app without passing access_scope produces an "all" app, not "owner". If you need to assert owner behaviour during a dev experiment, pass -AccessScope owner / --access-scope owner explicitly. The ring defaults live in LuminaProxyAPI.Cosmic/Config/appsettings.<ring>.json → AppProxyOptions.AccessScope.
sessionDuration: 2400 seconds (~40 minutes). After that the nohup process may keep running but the session won't honour new feedChars/exec calls.ISandboxKeepAliveService (touched once per GetAppContentAsync cache miss). Once the sandbox is gone, the {appId}.{BaseDomain} URL returns 404 Not Found from the handler.appId row in AppContentRegistration persists separately and is reused as long as the sandbox is alive.Tested against luminaserviceapi-b-4.luminadevaks-westus3.dev.copilotlumina.com:
Authorization header (or cookie), GETs to https://<appId>.<BaseDomain>/ return 302 to login.microsoftonline.com/.../oauth2/v2.0/authorize with state=<nonce> (no |-separated returnUrl), confirming the cookie-based state flow (AppProxyAuthStateCookie) is live.GET / returns 200 {"status":"ok"}.401 from the proxy → token oid mismatch on an owner-scoped app, or invalid/expired token.404 from the proxy → appId no longer registered or the sandbox is gone.Invoke-WebRequest -SkipCertificateCheck).curl and Python 3 on macOS/Linux (Python is used only to base64-encode the server payload and to splice JSON safely).<first 40 chars>...) when summarising.app_url to source control with the token alongside.server.py written to the sandbox is intentionally trivial. If you replace it with something else, remember the proxy will happily forward arbitrary requests once authorized, so don't expose anything you wouldn't expose publicly within the ring.tools
Acquire and validate Lumina EPS/LuminaServiceAPI bearer tokens using the CopilotLumina eps_client.py and get-lumina-token.ts helpers. Use when the user asks about Lumina token acquisition, EPS client authentication, testing luminaserviceapi hosts, running eps_client.py, validating v1/v3 EPS routes, resolving bundled helper script paths across .agents/.claude/.copilot installs, or fixing local Bun/Python/uv environment issues for these flows.
testing
Pulls 3-year financial statements (income, balance sheet, cash flow) for a single stock and produces a deep Chinese-language report, OR compares multiple peers head-to-head. Use whenever the user asks to 分析/解读/看一下 a company's 财报/财务/营收/利润/资产负债/现金流, asks for 三年/近三年/最近几年 financials, or wants to compare multiple stocks 对比/PK/比较 on financial metrics. Triggers on phrases like "帮我看下 X 的财报"、"分析 X 这三年的财务"、"X 和 Y 哪个更好"、"对比 A B C 三家". Covers US/HK/KR/A-share stocks via stockanalysis.com.
tools
End-to-end Microsoft Connect (half-yearly performance review) drafting + inject into the Connect tool. Trigger when user mentions Connect, connect draft, 绩效盘点, Microsoft performance review, "write my connect", "draft my connect", "帮我写 connect", or asks to populate v2.msconnect.microsoft.com. Gathers evidence (ADO work items + PRs, SharePoint-authored docs via workiq, historical Connects via Playwright for style), builds a local sign-off flow chart for the user to review, then injects HTML-formatted content (with hyperlinks, nested lists, underlines) directly into the Roosterjs rich-text editor fields via simulated paste events. Asks the user for period dates, repos, SharePoint URLs, and historical Connect IDs at runtime because these vary per person.
tools
Record work log / save work summary / add TODO items. TRIGGER when: user says '记录工作', '保存工作', 'record work', 'save work', 'log work', '工作记录', '写工作日志', '保存工作记录', '记录一下', or similar phrases about saving/recording what was done in the current session. Also trigger when user mentions work log, work record, 工作日志, or wants to summarize completed work for future performance review. Also trigger when user says '加一个todo', 'add a todo', '添加todo', '加个待办', '记录todo', or similar phrases about adding a TODO/待办 item to the work log.