Langsung ke konten utama

Documentation Index

Fetch the complete documentation index at: https://docs.openclaw.ai/llms.txt

Use this file to discover all available pages before exploring further.

Panduan ini menjelaskan cara membangun Plugin penyedia yang menambahkan penyedia model (LLM) ke OpenClaw. Pada akhirnya, Anda akan memiliki penyedia dengan katalog model, autentikasi kunci API, dan resolusi model dinamis.
Jika Anda belum pernah membangun Plugin OpenClaw sebelumnya, baca Memulai terlebih dahulu untuk struktur paket dasar dan penyiapan manifes.
Plugin penyedia menambahkan model ke loop inferensi normal OpenClaw. Jika model harus berjalan melalui daemon agen native yang memiliki thread, compaction, atau event alat, pasangkan penyedia dengan harness agen alih-alih menaruh detail protokol daemon di core.

Panduan Langkah demi Langkah

1

Paket dan manifes

Langkah 1: Paket dan manifes

{
  "name": "@myorg/openclaw-acme-ai",
  "version": "1.0.0",
  "type": "module",
  "openclaw": {
    "extensions": ["./index.ts"],
    "providers": ["acme-ai"],
    "compat": {
      "pluginApi": ">=2026.3.24-beta.2",
      "minGatewayVersion": "2026.3.24-beta.2"
    },
    "build": {
      "openclawVersion": "2026.3.24-beta.2",
      "pluginSdkVersion": "2026.3.24-beta.2"
    }
  }
}
Manifes mendeklarasikan providerAuthEnvVars agar OpenClaw dapat mendeteksi kredensial tanpa memuat runtime Plugin Anda. Tambahkan providerAuthAliases ketika varian penyedia harus menggunakan ulang autentikasi dari id penyedia lain. modelSupport bersifat opsional dan memungkinkan OpenClaw memuat otomatis Plugin penyedia Anda dari id model singkat seperti acme-large sebelum hook runtime tersedia. Jika Anda menerbitkan penyedia di ClawHub, field openclaw.compat dan openclaw.build tersebut wajib ada di package.json.
2

Daftarkan penyedia

Penyedia teks minimal memerlukan id, label, auth, dan catalog. catalog adalah hook runtime/konfigurasi yang dimiliki penyedia; ia dapat memanggil API vendor live dan mengembalikan entri models.providers.
index.ts
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth";

export default definePluginEntry({
  id: "acme-ai",
  name: "Acme AI",
  description: "Acme AI model provider",
  register(api) {
    api.registerProvider({
      id: "acme-ai",
      label: "Acme AI",
      docsPath: "/providers/acme-ai",
      envVars: ["ACME_AI_API_KEY"],

      auth: [
        createProviderApiKeyAuthMethod({
          providerId: "acme-ai",
          methodId: "api-key",
          label: "Acme AI API key",
          hint: "API key from your Acme AI dashboard",
          optionKey: "acmeAiApiKey",
          flagName: "--acme-ai-api-key",
          envVar: "ACME_AI_API_KEY",
          promptMessage: "Enter your Acme AI API key",
          defaultModel: "acme-ai/acme-large",
        }),
      ],

      catalog: {
        order: "simple",
        run: async (ctx) => {
          const apiKey =
            ctx.resolveProviderApiKey("acme-ai").apiKey;
          if (!apiKey) return null;
          return {
            provider: {
              baseUrl: "https://api.acme-ai.com/v1",
              apiKey,
              api: "openai-completions",
              models: [
                {
                  id: "acme-large",
                  name: "Acme Large",
                  reasoning: true,
                  input: ["text", "image"],
                  cost: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
                  contextWindow: 200000,
                  maxTokens: 32768,
                },
                {
                  id: "acme-small",
                  name: "Acme Small",
                  reasoning: false,
                  input: ["text"],
                  cost: { input: 1, output: 5, cacheRead: 0.1, cacheWrite: 1.25 },
                  contextWindow: 128000,
                  maxTokens: 8192,
                },
              ],
            },
          };
        },
      },
    });

    api.registerModelCatalogProvider({
      provider: "acme-ai",
      kinds: ["text"],
      liveCatalog: async (ctx) => {
        const apiKey = ctx.resolveProviderApiKey("acme-ai").apiKey;
        if (!apiKey) return null;
        return [
          {
            kind: "text",
            provider: "acme-ai",
            model: "acme-large",
            label: "Acme Large",
            source: "live",
          },
        ];
      },
    });
  },
});
registerModelCatalogProvider adalah surface katalog control-plane yang lebih baru untuk UI daftar/bantuan/pemilih. Gunakan ini untuk baris teks, pembuatan gambar, pembuatan video, dan pembuatan musik. Pertahankan panggilan endpoint vendor dan pemetaan respons di dalam Plugin; OpenClaw memiliki bentuk baris bersama, label sumber, dan rendering bantuan.Ini sudah menjadi penyedia yang berfungsi. Pengguna sekarang dapat menjalankan openclaw onboard --acme-ai-api-key <key> dan memilih acme-ai/acme-large sebagai model mereka.Jika penyedia upstream menggunakan token kontrol yang berbeda dari OpenClaw, tambahkan transformasi teks dua arah kecil alih-alih mengganti jalur stream:
api.registerTextTransforms({
  input: [
    { from: /red basket/g, to: "blue basket" },
    { from: /paper ticket/g, to: "digital ticket" },
    { from: /left shelf/g, to: "right shelf" },
  ],
  output: [
    { from: /blue basket/g, to: "red basket" },
    { from: /digital ticket/g, to: "paper ticket" },
    { from: /right shelf/g, to: "left shelf" },
  ],
});
input menulis ulang prompt sistem final dan konten pesan teks sebelum transport. output menulis ulang delta teks asisten dan teks final sebelum OpenClaw mem-parse marker kontrolnya sendiri atau pengiriman channel.Untuk penyedia bawaan yang hanya mendaftarkan satu penyedia teks dengan autentikasi kunci API plus satu runtime berbasis katalog, utamakan helper yang lebih sempit defineSingleProviderPluginEntry(...):
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";

export default defineSingleProviderPluginEntry({
  id: "acme-ai",
  name: "Acme AI",
  description: "Acme AI model provider",
  provider: {
    label: "Acme AI",
    docsPath: "/providers/acme-ai",
    auth: [
      {
        methodId: "api-key",
        label: "Acme AI API key",
        hint: "API key from your Acme AI dashboard",
        optionKey: "acmeAiApiKey",
        flagName: "--acme-ai-api-key",
        envVar: "ACME_AI_API_KEY",
        promptMessage: "Enter your Acme AI API key",
        defaultModel: "acme-ai/acme-large",
      },
    ],
    catalog: {
      buildProvider: () => ({
        api: "openai-completions",
        baseUrl: "https://api.acme-ai.com/v1",
        models: [{ id: "acme-large", name: "Acme Large" }],
      }),
      buildStaticProvider: () => ({
        api: "openai-completions",
        baseUrl: "https://api.acme-ai.com/v1",
        models: [{ id: "acme-large", name: "Acme Large" }],
      }),
    },
  },
});
buildProvider adalah jalur katalog live yang digunakan ketika OpenClaw dapat menyelesaikan autentikasi penyedia nyata. Ia dapat melakukan discovery khusus penyedia. Gunakan buildStaticProvider hanya untuk baris offline yang aman ditampilkan sebelum autentikasi dikonfigurasi; ia tidak boleh memerlukan kredensial atau membuat permintaan jaringan. Tampilan models list --all OpenClaw saat ini mengeksekusi katalog statis hanya untuk Plugin penyedia bawaan, dengan konfigurasi kosong, env kosong, dan tanpa path agen/workspace.Jika alur autentikasi Anda juga perlu menambal models.providers.*, alias, dan model default agen selama onboarding, gunakan helper preset dari openclaw/plugin-sdk/provider-onboard. Helper yang paling sempit adalah createDefaultModelPresetAppliers(...), createDefaultModelsPresetAppliers(...), dan createModelCatalogPresetAppliers(...).Ketika endpoint native penyedia mendukung blok penggunaan streamed pada transport openai-completions normal, utamakan helper katalog bersama di openclaw/plugin-sdk/provider-catalog-shared alih-alih melakukan hardcode pemeriksaan id penyedia. supportsNativeStreamingUsageCompat(...) dan applyProviderNativeStreamingUsageCompat(...) mendeteksi dukungan dari peta kapabilitas endpoint, sehingga endpoint native bergaya Moonshot/DashScope tetap ikut serta bahkan ketika Plugin menggunakan id penyedia kustom.
3

Tambahkan resolusi model dinamis

Jika penyedia Anda menerima ID model arbitrer (seperti proxy atau router), tambahkan resolveDynamicModel:
api.registerProvider({
  // ... id, label, auth, catalog from above

  resolveDynamicModel: (ctx) => ({
    id: ctx.modelId,
    name: ctx.modelId,
    provider: "acme-ai",
    api: "openai-completions",
    baseUrl: "https://api.acme-ai.com/v1",
    reasoning: false,
    input: ["text"],
    cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
    contextWindow: 128000,
    maxTokens: 8192,
  }),
});
Jika resolusi memerlukan panggilan jaringan, gunakan prepareDynamicModel untuk warm-up asinkron - resolveDynamicModel berjalan lagi setelah selesai.
4

Tambahkan hook runtime (sesuai kebutuhan)

Sebagian besar penyedia hanya memerlukan catalog + resolveDynamicModel. Tambahkan hook secara bertahap sesuai kebutuhan penyedia Anda.Builder helper bersama kini mencakup keluarga replay/tool-compat yang paling umum, sehingga Plugin biasanya tidak perlu merangkai setiap hook satu per satu secara manual:
import { buildProviderReplayFamilyHooks } from "openclaw/plugin-sdk/provider-model-shared";
import { buildProviderStreamFamilyHooks } from "openclaw/plugin-sdk/provider-stream";
import { buildProviderToolCompatFamilyHooks } from "openclaw/plugin-sdk/provider-tools";

const GOOGLE_FAMILY_HOOKS = {
  ...buildProviderReplayFamilyHooks({ family: "google-gemini" }),
  ...buildProviderStreamFamilyHooks("google-thinking"),
  ...buildProviderToolCompatFamilyHooks("gemini"),
};

api.registerProvider({
  id: "acme-gemini-compatible",
  // ...
  ...GOOGLE_FAMILY_HOOKS,
});
Keluarga replay yang tersedia saat ini:
KeluargaYang dihubungkanContoh bawaan
openai-compatibleKebijakan pemutaran ulang bergaya OpenAI bersama untuk transport yang kompatibel dengan OpenAI, termasuk sanitasi id panggilan alat, perbaikan pengurutan yang dimulai asisten, dan validasi giliran Gemini generik saat transport membutuhkannyamoonshot, ollama, xai, zai
anthropic-by-modelKebijakan pemutaran ulang yang sadar Claude yang dipilih oleh modelId, sehingga transport pesan Anthropic hanya mendapat pembersihan blok pemikiran khusus Claude saat model yang diselesaikan benar-benar merupakan id Claudeamazon-bedrock, anthropic-vertex
google-geminiKebijakan pemutaran ulang Gemini native ditambah sanitasi pemutaran ulang bootstrap dan mode keluaran penalaran bertaggoogle, google-gemini-cli
passthrough-geminiSanitasi tanda tangan pemikiran Gemini untuk model Gemini yang berjalan melalui transport proksi yang kompatibel dengan OpenAI; tidak mengaktifkan validasi pemutaran ulang Gemini native atau penulisan ulang bootstrapopenrouter, kilocode, opencode, opencode-go
hybrid-anthropic-openaiKebijakan hibrida untuk penyedia yang mencampur permukaan model pesan Anthropic dan yang kompatibel dengan OpenAI dalam satu plugin; penghapusan blok pemikiran khusus Claude opsional tetap dibatasi pada sisi Anthropicminimax
Keluarga aliran yang tersedia saat ini:
KeluargaYang dihubungkanContoh bawaan
google-thinkingNormalisasi payload pemikiran Gemini pada jalur aliran bersamagoogle, google-gemini-cli
kilocode-thinkingPembungkus penalaran Kilo pada jalur aliran proksi bersama, dengan kilo/auto dan id penalaran proksi yang tidak didukung melewati pemikiran yang disisipkankilocode
moonshot-thinkingPemetaan payload pemikiran native biner Moonshot dari konfigurasi + level /thinkmoonshot
minimax-fast-modePenulisan ulang model mode cepat MiniMax pada jalur aliran bersamaminimax, minimax-portal
openai-responses-defaultsPembungkus OpenAI/Codex Responses native bersama: header atribusi, /fast/serviceTier, verbositas teks, pencarian web Codex native, pembentukan payload kompatibilitas penalaran, dan manajemen konteks Responsesopenai, openai-codex
openrouter-thinkingPembungkus penalaran OpenRouter untuk rute proksi, dengan pelewatan model yang tidak didukung/auto ditangani secara terpusatopenrouter
tool-stream-default-onPembungkus tool_stream aktif secara default untuk penyedia seperti Z.AI yang menginginkan streaming alat kecuali dinonaktifkan secara eksplisitzai
Setiap pembangun keluarga disusun dari helper publik tingkat lebih rendah yang diekspor dari paket yang sama, yang dapat Anda gunakan saat penyedia perlu keluar dari pola umum:
  • openclaw/plugin-sdk/provider-model-shared - ProviderReplayFamily, buildProviderReplayFamilyHooks(...), dan pembangun pemutaran ulang mentah (buildOpenAICompatibleReplayPolicy, buildAnthropicReplayPolicyForModel, buildGoogleGeminiReplayPolicy, buildHybridAnthropicOrOpenAIReplayPolicy). Juga mengekspor helper pemutaran ulang Gemini (sanitizeGoogleGeminiReplayHistory, resolveTaggedReasoningOutputMode) dan helper endpoint/model (resolveProviderEndpoint, normalizeProviderId, normalizeGooglePreviewModelId).
  • openclaw/plugin-sdk/provider-stream - ProviderStreamFamily, buildProviderStreamFamilyHooks(...), composeProviderStreamWrappers(...), ditambah pembungkus OpenAI/Codex bersama (createOpenAIAttributionHeadersWrapper, createOpenAIFastModeWrapper, createOpenAIServiceTierWrapper, createOpenAIResponsesContextManagementWrapper, createCodexNativeWebSearchWrapper), pembungkus kompatibel OpenAI DeepSeek V4 (createDeepSeekV4OpenAICompatibleThinkingWrapper), pembersihan praisi pemikiran Anthropic Messages (createAnthropicThinkingPrefillPayloadWrapper), dan pembungkus proksi/penyedia bersama (createOpenRouterWrapper, createToolStreamWrapper, createMinimaxFastModeWrapper).
  • openclaw/plugin-sdk/provider-tools - ProviderToolCompatFamily, buildProviderToolCompatFamilyHooks("gemini"), dan helper skema Gemini yang mendasarinya (normalizeGeminiToolSchemas, inspectGeminiToolSchemas).
Beberapa helper aliran sengaja tetap lokal penyedia. @openclaw/anthropic-provider mempertahankan wrapAnthropicProviderStream, resolveAnthropicBetas, resolveAnthropicFastMode, resolveAnthropicServiceTier, dan pembangun pembungkus Anthropic tingkat lebih rendah dalam seam publik api.ts / contract-api.ts miliknya sendiri karena semuanya mengodekan penanganan beta OAuth Claude dan gating context1m. Plugin xAI juga mempertahankan pembentukan xAI Responses native dalam wrapStreamFn miliknya sendiri (alias /fast, tool_stream default, pembersihan strict-tool yang tidak didukung, penghapusan payload penalaran khusus xAI).Pola root paket yang sama juga mendukung @openclaw/openai-provider (pembangun penyedia, helper model default, pembangun penyedia realtime) dan @openclaw/openrouter-provider (pembangun penyedia ditambah helper onboarding/konfigurasi).
Untuk penyedia yang memerlukan pertukaran token sebelum setiap panggilan inferensi:
prepareRuntimeAuth: async (ctx) => {
  const exchanged = await exchangeToken(ctx.apiKey);
  return {
    apiKey: exchanged.token,
    baseUrl: exchanged.baseUrl,
    expiresAt: exchanged.expiresAt,
  };
},
OpenClaw memanggil hook dalam urutan ini. Sebagian besar penyedia hanya menggunakan 2-3: Field penyedia khusus kompatibilitas yang tidak lagi dipanggil OpenClaw, seperti ProviderPlugin.capabilities dan suppressBuiltInModel, tidak dicantumkan di sini.
#HookKapan digunakan
1catalogKatalog model atau default URL dasar
2applyConfigDefaultsDefault global milik penyedia selama materialisasi konfigurasi
3normalizeModelIdPembersihan alias id model legacy/pratinjau sebelum pencarian
4normalizeTransportPembersihan api / baseUrl keluarga penyedia sebelum perakitan model generik
5normalizeConfigMenormalkan konfigurasi models.providers.<id>
6applyNativeStreamingUsageCompatPenulisan ulang kompatibilitas penggunaan streaming native untuk penyedia konfigurasi
7resolveConfigApiKeyResolusi autentikasi penanda env milik penyedia
8resolveSyntheticAuthAutentikasi sintetis lokal/self-hosted atau berbasis konfigurasi
9shouldDeferSyntheticProfileAuthMenurunkan placeholder profil tersimpan sintetis di belakang autentikasi env/konfigurasi
10resolveDynamicModelMenerima ID model upstream arbitrer
11prepareDynamicModelPengambilan metadata asinkron sebelum menyelesaikan
12normalizeResolvedModelPenulisan ulang transport sebelum runner
13contributeResolvedModelCompatFlag kompatibilitas untuk model vendor di balik transport kompatibel lain
14normalizeToolSchemasPembersihan skema alat milik penyedia sebelum pendaftaran
15inspectToolSchemasDiagnostik skema alat milik penyedia
16resolveReasoningOutputModeKontrak keluaran penalaran bertag vs native
17prepareExtraParamsParameter permintaan default
18createStreamFnTransport StreamFn kustom sepenuhnya
19wrapStreamFnPembungkus header/isi khusus pada jalur aliran normal
20resolveTransportTurnStateHeader/metadata native per giliran
21resolveWebSocketSessionPolicyHeader/cool-down sesi WS native
22formatApiKeyBentuk token runtime khusus
23refreshOAuthRefresh OAuth khusus
24buildAuthDoctorHintPanduan perbaikan autentikasi
25matchesContextOverflowErrorDeteksi overflow milik penyedia
26classifyFailoverReasonKlasifikasi batas laju/kelebihan beban milik penyedia
27isCacheTtlEligibleGating TTL cache prompt
28buildMissingAuthMessagePetunjuk autentikasi hilang khusus
29augmentModelCatalogBaris forward-compat sintetis
30resolveThinkingProfileSet opsi /think khusus model
31isBinaryThinkingKompatibilitas pemikiran biner aktif/nonaktif
32supportsXHighThinkingKompatibilitas dukungan penalaran xhigh
33resolveDefaultThinkingLevelKompatibilitas kebijakan /think default
34isModernModelRefPencocokan model live/smoke
35prepareRuntimeAuthPertukaran token sebelum inferensi
36resolveUsageAuthPenguraian kredensial penggunaan khusus
37fetchUsageSnapshotEndpoint penggunaan khusus
38createEmbeddingProviderAdapter embedding milik penyedia untuk memori/pencarian
39buildReplayPolicyKebijakan pemutaran ulang/Compaction transkrip khusus
40sanitizeReplayHistoryPenulisan ulang pemutaran ulang khusus penyedia setelah pembersihan generik
41validateReplayTurnsValidasi giliran pemutaran ulang ketat sebelum runner tertanam
42onModelSelectedCallback pasca-pemilihan (mis. telemetri)
Catatan fallback runtime:
  • normalizeConfig memeriksa penyedia yang cocok terlebih dahulu, lalu plugin penyedia lain yang mendukung hook sampai ada yang benar-benar mengubah konfigurasi. Jika tidak ada hook penyedia yang menulis ulang entri konfigurasi keluarga Google yang didukung, normalizer konfigurasi Google bawaan tetap diterapkan.
  • resolveConfigApiKey menggunakan hook penyedia saat diekspos. Jalur amazon-bedrock bawaan juga memiliki resolver penanda env AWS bawaan di sini, meskipun autentikasi runtime Bedrock sendiri tetap menggunakan rantai default AWS SDK.
  • resolveSystemPromptContribution memungkinkan penyedia menyisipkan panduan prompt sistem yang sadar cache untuk keluarga model. Pilih ini daripada before_prompt_build saat perilaku menjadi milik satu keluarga penyedia/model dan harus mempertahankan pemisahan cache stabil/dinamis.
Untuk deskripsi terperinci dan contoh dunia nyata, lihat Internal: Hook Runtime Penyedia.
5

Tambahkan kapabilitas ekstra (opsional)

Langkah 5: Tambahkan kapabilitas ekstra

Plugin penyedia dapat mendaftarkan speech, transkripsi realtime, suara realtime, pemahaman media, pembuatan gambar, pembuatan video, web fetch, dan pencarian web bersama inferensi teks. OpenClaw mengklasifikasikan ini sebagai plugin hybrid-capability - pola yang direkomendasikan untuk plugin perusahaan (satu plugin per vendor). Lihat Internal: Kepemilikan Kapabilitas.Daftarkan setiap kapabilitas di dalam register(api) bersama panggilan api.registerProvider(...) yang sudah ada. Pilih hanya tab yang Anda perlukan:
import {
  assertOkOrThrowProviderError,
  postJsonRequest,
} from "openclaw/plugin-sdk/provider-http";

api.registerSpeechProvider({
  id: "acme-ai",
  label: "Acme Speech",
  isConfigured: ({ config }) => Boolean(config.messages?.tts),
  synthesize: async (req) => {
    const { response, release } = await postJsonRequest({
      url: "https://api.example.com/v1/speech",
      headers: new Headers({ "Content-Type": "application/json" }),
      body: { text: req.text },
      timeoutMs: req.timeoutMs,
      fetchFn: fetch,
      auditContext: "acme speech",
    });
    try {
      await assertOkOrThrowProviderError(response, "Acme Speech API error");
      return {
        audioBuffer: Buffer.from(await response.arrayBuffer()),
        outputFormat: "mp3",
        fileExtension: ".mp3",
        voiceCompatible: false,
      };
    } finally {
      await release();
    }
  },
});
Gunakan assertOkOrThrowProviderError(...) untuk kegagalan HTTP penyedia agar plugin berbagi pembacaan isi galat yang dibatasi, parsing galat JSON, dan sufiks ID permintaan.
6

Uji

Langkah 6: Uji

src/provider.test.ts
import { describe, it, expect } from "vitest";
// Export your provider config object from index.ts or a dedicated file
import { acmeProvider } from "./provider.js";

describe("acme-ai provider", () => {
  it("resolves dynamic models", () => {
    const model = acmeProvider.resolveDynamicModel!({
      modelId: "acme-beta-v3",
    } as any);
    expect(model.id).toBe("acme-beta-v3");
    expect(model.provider).toBe("acme-ai");
  });

  it("returns catalog when key is available", async () => {
    const result = await acmeProvider.catalog!.run({
      resolveProviderApiKey: () => ({ apiKey: "test-key" }),
    } as any);
    expect(result?.provider?.models).toHaveLength(2);
  });

  it("returns null catalog when no key", async () => {
    const result = await acmeProvider.catalog!.run({
      resolveProviderApiKey: () => ({ apiKey: undefined }),
    } as any);
    expect(result).toBeNull();
  });
});

Publikasikan ke ClawHub

Plugin penyedia dipublikasikan dengan cara yang sama seperti plugin kode eksternal lainnya:
clawhub package publish your-org/your-plugin --dry-run
clawhub package publish your-org/your-plugin
Jangan gunakan alias publikasi lama yang hanya untuk skill di sini; paket plugin harus menggunakan clawhub package publish.

Struktur file

<bundled-plugin-root>/acme-ai/
├── package.json              # openclaw.providers metadata
├── openclaw.plugin.json      # Manifest with provider auth metadata
├── index.ts                  # definePluginEntry + registerProvider
└── src/
    ├── provider.test.ts      # Tests
    └── usage.ts              # Usage endpoint (optional)

Referensi urutan katalog

catalog.order mengontrol kapan katalog Anda digabungkan relatif terhadap penyedia bawaan:
UrutanKapanKasus penggunaan
simplePass pertamaPenyedia API key biasa
profileSetelah simplePenyedia yang dibatasi profil auth
pairedSetelah profileMensintesis beberapa entri terkait
latePass terakhirMenimpa penyedia yang ada (menang saat konflik)

Langkah berikutnya

Terkait