mirror of
https://github.com/HeyPuter/puter.git
synced 2026-10-17 09:09:43 +00:00
* feat: add BytePlus ModelArk chat provider Adds BytePlus ModelArk as a provider for the puter-chat-completion driver, following the MiniMax/ZAI providers as reference per doc/contributing-apis.md. - OpenAI-compatible endpoint at ark.ap-southeast.bytepluses.com/api/v3 (apiBaseUrl config selects the region) - Static catalog of 16 chat models (Seed 2.x/1.x incl. vision, GLM, DeepSeek, GPT-OSS) with limits and per-token pricing from the official docs - Passes Ark's thinking/response_format/stop params through custom; normalizes reasoning_content to reasoning - Bare deepseek-v4-* names stay with the first-party DeepSeek provider; BytePlus only claims prefixed aliases - Offline unit tests (mocked SDK against a real test server) plus an env-gated integration test * feat: add BytePlus image and video providers Extends the BytePlus ModelArk integration to the puter-image-generation and puter-video-generation drivers, reusing the same services.byteplus API key and regional apiBaseUrl as the chat provider. Image (Seedream/SeedEdit via OpenAI-compatible /images/generations): - dola-seedream-5-0-pro (pixel-tier pricing + billed input images from the 2nd on), seedream-5-0-lite, 4-5, 4-0, and seededit-3-0-i2i - quality tiers 1K/1.5K/2K; aspect ratios resolve to Ark's documented pixel sizes; explicit WxH passes through with Ark's bounds enforced Video (Seedance via Ark's async /contents/generations/tasks + polling): - Seedance 2.0 / 2.0 Fast / 2.0 Mini / 1.5 Pro / 1.0 Pro / 1.0 Pro Fast (2.5 is priced but its API isn't live yet, so it's excluded) - per-video-token billing from usage.completion_tokens, with per-second estimates feeding the credit cap; audio vs silent rates for 1.5 Pro - first/last frame and reference-image inputs; generate_audio param added to IGenerateVideoParams Pricing and capabilities hardcoded from the official docs (ModelArk pages 1544106, 1330310, 1520757, 1521309, 1541523). Offline unit tests mock the SDK / global fetch; integration tests are env-gated on PUTER_TEST_AI_BYTEPLUS_API_KEY. * fix: correct BytePlus catalogs and validation against the live API Verified the three BytePlus providers against ModelArk with a real key; these are the mismatches that surfaced. - Drop seededit-3-0-i2i-250628. Ark reports it as Shutdown and every request 404s. Its now-unreachable image-to-image branches in the provider go with it. - seedream-4-5 and the 5.0 series enforce a 3,686,400 pixel minimum, so they only accept the 2K tier. Mark them 2k-only and snap an unsupported tier up to the nearest allowed one, which also keeps the aspect-ratio table from mapping to a sub-minimum size. - glm-4-7 has a 204,800 token context, not 256K. - Guard the actor in the image provider like the video provider does. - Round a sub-minimum video duration up to the shortest supported clip instead of reporting it as insufficient funds. - Gate video resolution on the model's own dimensions; the dims table is shared across a family and accepts more than any one model does. * Tighten BytePlus AI provider handling Extract shared reasoning-content normalization for OpenAI-style chat providers, and harden BytePlus image/video behavior. This updates image tier and size validation, normalizes aspect ratios and input image refs, prevents mismatched BytePlus key/base URL fallback config, makes video resolution matching case-insensitive, and rejects excess reference images instead of silently truncating them. Tests were expanded to cover the new BytePlus request and validation paths.
474 lines
21 KiB
JSON
474 lines
21 KiB
JSON
{
|
|
// Comprehensive template — every key the OSS backend or shipped (non-prod)
|
|
// extensions read. Copy to `config.json` and trim what you don't need;
|
|
// unset keys fall back to documented defaults. See
|
|
// `src/backend/types.ts` for the per-field source of truth.
|
|
//
|
|
// Each setting lives at exactly one canonical key — there are no fallback
|
|
// aliases. Values shown are illustrative, not production secrets.
|
|
//
|
|
// Keys consumed only by closed-source / hosted-prod extensions (clickhouse,
|
|
// cacheUpdateHandler, pages, prodMeteringAndBilling, …) are intentionally
|
|
// omitted.
|
|
|
|
// ── Environment / identity ──────────────────────────────────────────
|
|
"config_name": "template",
|
|
// `dev` opens a browser on boot, skips blocked-email checks, and runs the
|
|
// dev-time webpack watcher; `prod` serves pre-built bundles.
|
|
"env": "dev",
|
|
// Console output format. `json` replaces the global console so every call
|
|
// emits one structured JSON line (level, timestamp, msg, and the active
|
|
// request's trace id) — one event per call, so a line-oriented log
|
|
// collector can't split stack traces across events and level filtering
|
|
// works. Unset (the default) leaves console output human-readable.
|
|
"log_format": "text",
|
|
"version": "0.0.0",
|
|
// Stable identity for this server node — used by pager alerts and
|
|
// graceful-shutdown coordination.
|
|
"serverId": "node-1",
|
|
|
|
// ── Networking / URLs ───────────────────────────────────────────────
|
|
// Port Puter listens on internally.
|
|
"port": 4100,
|
|
// Externally-visible port (set this when behind a reverse proxy on 80/443).
|
|
"pub_port": 4100,
|
|
"protocol": "http",
|
|
"domain": "puter.localhost",
|
|
// Fully-qualified externally-visible URL. Computed from protocol/domain/
|
|
// pub_port if unset.
|
|
"origin": "http://puter.localhost:4100",
|
|
// Public base URL for the API subdomain (used to build signed URLs and
|
|
// surfaced to the client by the `installedApps` and `whoami` extensions).
|
|
"api_base_url": "http://api.puter.localhost:4100",
|
|
// Subdomains Puter routes on. Wildcard DNS (`*.<domain>`) must point at
|
|
// this server for site/app hosting to work.
|
|
"static_hosting_domain": "site.puter.localhost",
|
|
"static_hosting_domain_alt": "host.puter.localhost",
|
|
"private_app_hosting_domain": "app.puter.localhost",
|
|
"private_app_hosting_domain_alt": "dev.puter.localhost",
|
|
// Host-header / domain handling. Defaults below are the dev-friendly
|
|
// settings; tighten for any public install.
|
|
"allow_all_host_values": true,
|
|
"allow_no_host_header": true,
|
|
"allow_nipio_domains": false,
|
|
"custom_domains_enabled": false,
|
|
"enable_ip_validation": false,
|
|
// Express `trust proxy` setting — set to the number of reverse-proxy
|
|
// hops in front of the server (1 = nginx/Cloudflare, 2 = CF→ALB→app),
|
|
// or to a CIDR / IP / list. `false` (safe default) makes `req.ip` the
|
|
// direct socket peer. NEVER set to `true` in prod — it trusts every hop
|
|
// and makes X-Forwarded-For forgeable.
|
|
"trust_proxy": false,
|
|
"no_browser_launch": false,
|
|
|
|
// ── Dev watcher (devWatcher extension) ──────────────────────────────
|
|
// Rebuilds GUI + puter.js on file changes when running from source.
|
|
// Ignored when `env: "prod"` unless `devwatch.enabled: true`.
|
|
"no_devwatch": false,
|
|
"devwatch": {
|
|
// Delay after watcher startup before boot continues. Lets webpack
|
|
// emit its first build so the homepage doesn't 404 on bundle.min.js.
|
|
"ready_delay_ms": 5000
|
|
},
|
|
|
|
// ── Auth / session ──────────────────────────────────────────────────
|
|
// ALWAYS replace these for any public install — `openssl rand -hex 64`.
|
|
"jwt_secret_v2": "change-me",
|
|
"url_signature_secret": "change-me",
|
|
"cookie_name": "puter_auth_token",
|
|
"min_pass_length": 6,
|
|
// When true, anonymous users must log in instead of creating temp or
|
|
// permanent accounts.
|
|
"disable_user_signup": false,
|
|
"allow_system_login": false,
|
|
"strict_email_verification_required": false,
|
|
"captcha": {
|
|
"enabled": false,
|
|
"difficulty": "medium"
|
|
},
|
|
"oidc": {
|
|
"providers": {
|
|
// Google uses OIDC discovery — only ids are required.
|
|
"google": {
|
|
"client_id": "",
|
|
"client_secret": "",
|
|
"scopes": "openid email profile"
|
|
},
|
|
// Custom OIDC providers must also supply the three endpoints.
|
|
"custom-oidc": {
|
|
"client_id": "",
|
|
"client_secret": "",
|
|
"authorization_endpoint": "",
|
|
"token_endpoint": "",
|
|
"userinfo_endpoint": "",
|
|
"scopes": "openid email profile"
|
|
}
|
|
}
|
|
},
|
|
|
|
// ── Groups / provisioning ───────────────────────────────────────────
|
|
// UIDs of the persistent groups new users are auto-enrolled in.
|
|
"default_user_group": "78b1b1dd-c959-44d2-b02c-8735671f9997",
|
|
"default_temp_group": "b7220104-7905-4985-b996-649fdcdb3c8f",
|
|
// When true, ACL grants read/list on `/<user>/Public` to any actor.
|
|
"enable_public_folders": true,
|
|
|
|
// ── Storage / S3 ────────────────────────────────────────────────────
|
|
"s3": {
|
|
// Local fauxqs (in-process S3-compatible) — used in dev and the
|
|
// bundled-defaults Docker mode. Files land under `dataDir`.
|
|
"localConfig": {
|
|
"inMemory": false,
|
|
"host": "127.0.0.1",
|
|
"port": 4566,
|
|
"dataDir": "volatile/runtime/fauxqs-data",
|
|
"s3StorageDir": "volatile/runtime/fauxqs-s3-data"
|
|
},
|
|
// For real / external S3, replace `localConfig` above with `s3Config`.
|
|
"_remote_example": {
|
|
"s3Config": {
|
|
"useCredentialChain": false,
|
|
"endpoint": "https://s3.example.com",
|
|
// Endpoint used in presigned URLs handed to the browser. Set
|
|
// this when the server-side endpoint isn't reachable from the
|
|
// browser (e.g. docker-internal `http://s3:9000`).
|
|
"publicEndpoint": "",
|
|
"accessKeyId": "",
|
|
"secretAccessKey": "",
|
|
"region": "us-west-2",
|
|
// Set true for RustFS / MinIO / fauxqs (path-style URLs).
|
|
// Real AWS S3 wants virtual-hosted — leave unset / false.
|
|
"forcePathStyle": false
|
|
}
|
|
}
|
|
},
|
|
"s3_bucket": "puter-local",
|
|
"s3_region": "us-west-2",
|
|
"region": "us-west-2",
|
|
// Default per-user storage cap (bytes). 100 MB.
|
|
"storage_capacity": 104857600,
|
|
"is_storage_limited": false,
|
|
"available_device_storage": 0,
|
|
// ── Thumbnails (thumbnails extension) ───────────────────────────────
|
|
// Optional dedicated S3-compatible bucket for generated thumbnails.
|
|
// When unset (or `endpoint` empty), the extension falls back to the
|
|
// main S3 client / bucket above.
|
|
"thumbnailStore": {
|
|
"name": "puter-local",
|
|
"endpoint": "",
|
|
"credentials": {
|
|
"accessKeyId": "",
|
|
"secretAccessKey": ""
|
|
}
|
|
},
|
|
|
|
// ── Database ────────────────────────────────────────────────────────
|
|
"database": {
|
|
// `sqlite` for single-node/dev, `mysql` for MariaDB/MySQL,
|
|
// or `postgres` for PostgreSQL.
|
|
//
|
|
// NOTE: `postgres` support is a community contribution and is not
|
|
// exercised by Puter.com production. It boots, passes its own
|
|
// integration tests against pgmock, and runs the common user/app/
|
|
// fsentry/session/permission/OIDC flows — but less-traveled code
|
|
// paths may surface MySQL/SQLite-isms that haven't been ported yet.
|
|
// Expect rough edges and please file issues if you hit one. For
|
|
// production self-hosting today, `mysql` (MariaDB) and `sqlite` are
|
|
// the supported defaults.
|
|
"engine": "sqlite",
|
|
// sqlite — file path on disk
|
|
"path": "volatile/runtime/puter-database.sqlite",
|
|
"targetVersion": 0,
|
|
// mysql/postgres — connection details. PostgreSQL defaults to port
|
|
// 5432 when `engine` is `postgres`; MySQL/MariaDB normally use 3306.
|
|
"host": "",
|
|
"port": 3306,
|
|
"user": "",
|
|
"password": "",
|
|
"database": "",
|
|
// postgres may also use a URL instead of host/user/password fields:
|
|
// "connectionString": "postgres://puter:secret@localhost:5432/puter",
|
|
// Optional read-replica pool. Reads route here when populated.
|
|
"replica": {
|
|
"host": "",
|
|
"port": 3306,
|
|
"user": "",
|
|
"password": "",
|
|
"database": ""
|
|
}
|
|
// mysql/postgres self-host bootstrap: set `migrationPaths` to apply the
|
|
// bundled schema on first boot. Idempotent — safe to leave on.
|
|
// "migrationPaths": ["./src/backend/clients/database/migrations/mysql"]
|
|
// "migrationPaths": ["./src/backend/clients/database/migrations/postgres"]
|
|
},
|
|
|
|
// ── DynamoDB (KV store) ─────────────────────────────────────────────
|
|
"dynamo": {
|
|
// Local emulator (dynamodb-local) endpoint. Drop this field for real
|
|
// AWS DynamoDB.
|
|
"endpoint": "http://localhost:8000",
|
|
// Set true when pointing at a local emulator so Puter creates the KV
|
|
// table on boot. NEVER set against real AWS — provision via IaC.
|
|
// "bootstrapTables": true,
|
|
"path": "",
|
|
// Credentials. NOTE: snake_case here, unlike `s3.s3Config` below.
|
|
// For dynamodb-local, any non-empty values work.
|
|
"aws": {
|
|
"access_key": "",
|
|
"secret_key": "",
|
|
"region": "us-west-2"
|
|
}
|
|
},
|
|
|
|
// ── Redis / Valkey (cache + cross-node rate limit) ──────────────────
|
|
"redis": {
|
|
// True → in-process redis-mock (dev / single-node).
|
|
"useMock": true,
|
|
// Cluster nodes for ioredis. For a single Valkey/Redis container,
|
|
// run it in cluster mode (one node, all slots).
|
|
"startupNodes": [
|
|
{
|
|
"host": "127.0.0.1",
|
|
"port": 7000
|
|
}
|
|
]
|
|
// Defaults to true (matches prod ElastiCache). Set false for plain-TCP
|
|
// self-host Valkey/Redis.
|
|
// "tls": false
|
|
},
|
|
|
|
// ── Email (transactional) ───────────────────────────────────────────
|
|
// Nodemailer transport — used for password resets, email confirmation, etc.
|
|
"email": {
|
|
"from": "\"Puter\" <no-reply@puter.com>",
|
|
"host": "smtp.example.com",
|
|
"port": 587,
|
|
"secure": false,
|
|
"service": "",
|
|
"auth": {
|
|
"user": "",
|
|
"pass": ""
|
|
}
|
|
},
|
|
|
|
// ── Alarms / alerting ───────────────────────────────────────────────
|
|
// Where system alarms go. Severity is the routing decision — each
|
|
// transport takes everything at or above its own `minSeverity`:
|
|
//
|
|
// critical — an unhandled server error; pages on-call.
|
|
// error — pages as well; prefer critical or warning.
|
|
// warning — look at it today; no page.
|
|
// info — a record in the chat channel only.
|
|
//
|
|
// Both transports are off unless enabled, so a self-hosted node just
|
|
// logs its alarms to the console.
|
|
"pager": {
|
|
// Severity for call sites that don't pick one. Default "critical".
|
|
"defaultSeverity": "critical",
|
|
|
|
// Retier or silence an alarm without a deploy. Keys are alarm ids,
|
|
// or a prefix ending in `*`; the exact id wins over a pattern, and
|
|
// the longest matching pattern wins among patterns. Values are a
|
|
// severity or "mute". This is applied last, so it overrides both the
|
|
// call site and any known-error rule.
|
|
"severityOverrides": {
|
|
// "cronMonitor:*": "info",
|
|
// "http_500:GET:/some/flapping/route:*": "mute"
|
|
},
|
|
|
|
"pagerduty": {
|
|
"enabled": false,
|
|
"routingKey": "",
|
|
// Lowest severity that reaches PagerDuty. Default "warning",
|
|
// which keeps `info` out of the paging system entirely.
|
|
"minSeverity": "warning"
|
|
},
|
|
|
|
// Slack incoming webhook — the low-noise destination for everything
|
|
// that shouldn't page.
|
|
"slack": {
|
|
"enabled": false,
|
|
"webhookUrl": "",
|
|
// Optional; defaults to the channel the webhook was created for.
|
|
"channel": "#alerts",
|
|
"username": "puter-alarms",
|
|
// Severity window posted to Slack. The ceiling defaults to
|
|
// "info" when PagerDuty is configured — what pages belongs in
|
|
// the pager, not in chat — and to "critical" when Slack is the
|
|
// only transport.
|
|
"minSeverity": "info",
|
|
"maxSeverity": "info",
|
|
// Don't repost the same alarm id within this window. The first
|
|
// occurrence always posts; the next post that gets through
|
|
// reports how many occurrences piled up. 0 disables throttling.
|
|
"repeatThrottleMs": 900000
|
|
}
|
|
},
|
|
|
|
// ── Rate limiting ───────────────────────────────────────────────────
|
|
// `memory` for single-node, `redis` for multi-node (default), `kv` for
|
|
// dynamo-backed counters.
|
|
"rate_limit": {
|
|
"backend": "redis"
|
|
},
|
|
|
|
// ── AI / integration providers ──────────────────────────────────────
|
|
// All AI drivers (chat, image, video, TTS, OCR, STT) read from here.
|
|
// Provider id == driver-side identifier. Leave empty / omit to disable.
|
|
"providers": {
|
|
// ─ Chat / completion ─
|
|
"claude": { "apiKey": "" },
|
|
"openai-completion": { "apiKey": "" },
|
|
"azure-openai": {
|
|
"apiKey": "",
|
|
"apiURL": ""
|
|
},
|
|
"gemini": { "apiKey": "" },
|
|
"groq": { "apiKey": "" },
|
|
"deepseek": { "apiKey": "" },
|
|
"mistral": { "apiKey": "" },
|
|
"xai": { "apiKey": "" },
|
|
"moonshot": { "apiKey": "" },
|
|
"minimax": {
|
|
"apiKey": "",
|
|
"apiBaseUrl": "https://api.minimax.io/v1"
|
|
},
|
|
"openrouter": {
|
|
"apiKey": "",
|
|
"apiBaseUrl": "https://openrouter.ai/api/v1"
|
|
},
|
|
"infron": {
|
|
"apiKey": "",
|
|
"apiBaseUrl": "https://llm.onerouter.pro/v1"
|
|
},
|
|
// BytePlus ModelArk. One key powers chat (Seed/GLM/DeepSeek), image
|
|
// generation (Seedream) and video generation (Seedance); `apiBaseUrl`
|
|
// selects the region; see
|
|
// https://docs.byteplus.com/en/docs/ModelArk/1330310 for options.
|
|
"byteplus": {
|
|
"apiKey": "",
|
|
"apiBaseUrl": "https://ark.ap-southeast.bytepluses.com/api/v3"
|
|
},
|
|
"zai": { "apiKey": "" },
|
|
"alibaba": { "apiKey": "" },
|
|
"together-ai": { "apiKey": "" },
|
|
// Local Ollama. `enabled: false` skips the auto-probe at startup
|
|
// (otherwise Puter logs ECONNREFUSED on every boot when no Ollama
|
|
// is running).
|
|
"ollama": {
|
|
"enabled": false,
|
|
"apiBaseUrl": "http://localhost:11434"
|
|
},
|
|
|
|
// ─ Image generation ─
|
|
"openai-image-generation": { "apiKey": "" },
|
|
"gemini-image-generation": { "apiKey": "" },
|
|
"together-image-generation": { "apiKey": "" },
|
|
"cloudflare-image-generation": {
|
|
"apiToken": "",
|
|
"accountId": "",
|
|
"apiBaseUrl": "https://api.cloudflare.com/client/v4"
|
|
},
|
|
"xai-image-generation": { "apiKey": "" },
|
|
|
|
// ─ Video generation ─
|
|
"openai-video-generation": { "apiKey": "" },
|
|
"together-video-generation": { "apiKey": "" },
|
|
"gemini-video-generation": { "apiKey": "" },
|
|
|
|
// ─ Speech / OCR ─
|
|
"openai": { "apiKey": "" },
|
|
"elevenlabs": {
|
|
"apiKey": "",
|
|
"apiBaseUrl": "https://api.elevenlabs.io",
|
|
"defaultVoiceId": "",
|
|
"speechToSpeechModelId": ""
|
|
},
|
|
"aws-polly": {
|
|
"access_key": "",
|
|
"secret_key": "",
|
|
"region": "us-west-2"
|
|
},
|
|
"speechify": { "apiKey": "" },
|
|
"aws-textract": {
|
|
"access_key": "",
|
|
"secret_key": "",
|
|
"region": "us-west-2"
|
|
},
|
|
"mistral-ocr": { "apiKey": "" }
|
|
},
|
|
|
|
// ── GUI / static mounts ─────────────────────────────────────────────
|
|
"gui_assets_root": "./src/gui",
|
|
"gui_profile": "development",
|
|
"builtin_apps": {
|
|
"dev-center": "./src/dev-center"
|
|
},
|
|
// Force the bundled GUI even in dev — set true when running from a
|
|
// pre-built tree without webpack-dev-server.
|
|
"use_bundled_gui": false,
|
|
"gui_bundle": "/dist/bundle.min.js",
|
|
"gui_css": "/dist/bundle.min.css",
|
|
"gui_puterjs_bundle": "https://js.puter.com/v2/",
|
|
"gui_params": {
|
|
"title": "Puter",
|
|
"short_description": "Your personal cloud computer",
|
|
"social_media_image": ""
|
|
},
|
|
// Optional roots for native app bundles and custom puter.js builds.
|
|
"native_apps_root": "",
|
|
"client_libs_root": "",
|
|
"puterjs_root": "./src/puter-js/dist",
|
|
|
|
// ── Feature flags (whoami extension) ────────────────────────────────
|
|
// Flat `{ flag_name: boolean }` bag. Server-only by default — flags are
|
|
// only surfaced to the client if their key is on the allowlist in
|
|
// `extensions/whoami.ts` (CLIENT_VISIBLE_FEATURE_FLAGS).
|
|
"feature_flags": {
|
|
"example_flag": false
|
|
},
|
|
|
|
// ── Misc / safety ───────────────────────────────────────────────────
|
|
// TLDs / domains rejected at signup (prod only).
|
|
"blockedEmailDomains": [],
|
|
"support_email": "support@puter.com",
|
|
// Worker / subdomain names users can't claim.
|
|
"reserved_words": [],
|
|
"max_subdomains_per_user": 10,
|
|
"server_health": {
|
|
"db_liveness_latency_fail_ms": 1500,
|
|
"stale_health_loop_fail_ms": 0
|
|
},
|
|
|
|
// ── Extensions ──────────────────────────────────────────────────────
|
|
// Directories scanned for extension entrypoints (`*.ts` / subdirs).
|
|
"extensions": [
|
|
"./extensions"
|
|
],
|
|
|
|
// ── Metering ────────────────────────────────────────────────────────
|
|
// When true, every account resolves to an unlimited policy: usage is still
|
|
// recorded, but nothing is ever refused for lack of budget. This is the
|
|
// setting for a deployment with no way to buy more — without it, accounts
|
|
// are held to the free monthly allowance and start getting 402s from the AI
|
|
// surfaces, file transfers and KV once they pass it.
|
|
"unlimitedMetering": false,
|
|
|
|
// Whether an account that has spent its whole allowance is refused the
|
|
// operations that spend it — file transfers, KV calls. Recording is
|
|
// unaffected either way. `workers` extends the same refusal to
|
|
// worker-driven calls, which are exempt by default because a deployed
|
|
// worker has nowhere to surface a payment prompt. `subscriptions` covers
|
|
// the separate question of surfaces reserved for paid plans (the
|
|
// vendor-compatible AI endpoints): turn it off on a deployment with no
|
|
// paid plans, or every account is refused them.
|
|
// "meteringEnforcement": { "enabled": true, "workers": false, "subscriptions": true },
|
|
|
|
// Fleet-wide spend rate, in micro-cents per minute, past which metering
|
|
// raises the `metering:excessiveGlobalUsageRate` alarm. Omit it (the
|
|
// default) to leave the check off: the only useful value is a multiple of
|
|
// what this deployment's normal traffic costs, so it has to be measured
|
|
// rather than guessed, and a stale number here alarms on healthy growth.
|
|
// "maxGlobalUsagePerMinute": 200000000
|
|
}
|