Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Manus Ai Php Laravel Package

tigusigalpa/manus-ai-php

A PHP client for the Manus AI API that makes it easy to integrate Manus AI agents and tasks into Laravel or any PHP application. Install via Composer, configure your API key, and start automating workflows with a clean, simple interface.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish

composer require <vendor>/manus-php-sdk   # requires PHP 8.2+ per CI matrix
php artisan vendor:publish --tag=manus-config

Then set credentials in .env. The SDK supports two auth modes, so pick one:

MANUS_AUTH_MODE=api_key        # or: oauth
MANUS_API_KEY=sk-...
# MANUS_OAUTH_TOKEN=...        # when MANUS_AUTH_MODE=oauth

Where to look first

  1. config/manus.php — base URL, auth mode, timeout, response-size guard, default agent_profile. Everything you will want to tune lives here.
  2. The service provider bindings — resolve the client from the container rather than new-ing it, because the bindings are what carry config and auth mode.
  3. README + examples/ (rewritten in 2.1.0, EN and RU) — the migration guide there is the fastest way to see the v2 field renames.
  4. Artisan commands (php artisan list and grep for the package namespace) — useful for smoke-testing credentials before writing code.

First use case: create a task

use Manus\Laravel\Facades\Manus;

$task = Manus::tasks()->create([
    'prompt'         => 'Summarise this quarter's churn drivers',
    'agent_profile'  => 'standard',   // standard | lite | max
    'interactive_mode' => false,
]);

// Responses are normalised, so read through the envelope rather than
// guessing at raw API shape:
$id = $task->id();          // also available via $task['task']['detail']['id']

Run it in php artisan tinker first. If auth is wrong you'll get a clear API error rather than a silent null, which makes tinker the cheapest place to validate the whole chain.

Implementation Patterns

Resolve the client, don't construct it. Inject the client interface into jobs, actions, and controllers. Facade access is fine in controllers and Artisan commands; in queued jobs prefer constructor injection so you can swap in a fake in tests and so a long-lived worker doesn't hold stale state.

final class CreateManusTask
{
    public function __construct(private ManusClient $manus) {}

    public function handle(Report $report): string
    {
        return $this->manus->tasks()->create([
            'prompt'        => $report->prompt(),
            'agent_profile' => config('manus.default_profile', 'standard'),
        ])->id();
    }
}

Multi-tenant / per-user tokens. The config file is a single-tenant default. For per-user OAuth tokens, bind a scoped instance in a service provider or a small factory that takes an explicit token, and never mutate the singleton's config at runtime — with queue workers that leaks across jobs.

Wrap task creation in an action, store the task ID. Task IDs are your handle for polling, updates, and webhook correlation. Persist task_id alongside your own model, plus the agent profile and prompt, so you can reproduce and audit later. The normalised response means you should read the ID once at creation and store it — don't re-derive it from array paths downstream.

Webhooks over polling. With the new webhook endpoints, register a webhook and handle it in a dedicated controller that: verifies the request, dispatches a job, and returns 200 immediately. Keep the payload in the job (or persist it) rather than doing work inline — Manus will retry on non-2xx and slow handlers will cause duplicate deliveries.

Route::post('/webhooks/manus', ManusWebhookController::class)
    ->withoutMiddleware(['csrf']);

Normalise at the boundary. Because 2.1.0 normalises task, file, and webhook envelopes (including the nested task.detail), your domain code should never touch ['task']['detail']. Put a single mapper between SDK responses and your own DTOs; when the envelope shifts again, one file changes.

Queue everything that talks to the API. Network calls in a request cycle will bite you on timeouts. ShouldQueue jobs with $tries = 3 and a backoff of [10, 60, 300] handles most transient failures; treat 4xx as non-retryable via $this->fail() in the exception handler.

Testing. Use Laravel's HTTP fake against the underlying client, or bind a fake implementation of the client interface. Assert on the payload your app sends (field names, profile values) — that's where the v2 renames will actually break you — rather than asserting on SDK internals. The package ships 40 tests / 86 assertions, which is thin coverage for the surface area; don't assume untested endpoints behave.

Artisan for operations. Use the provided commands for one-off discovery and usage checks in a deploy or a scheduled job, rather than writing bespoke scripts. usage endpoints in particular are a good fit for a nightly scheduled command that writes to a metrics table.

Gotchas and Tips

listFiles() is gone. Manus API v2 has no file-list endpoint, so any code that enumerated uploads must be replaced. Track uploaded file IDs in your own table at upload time; there is no way to reconcile after the fact. This is the one genuinely breaking removal in the release.

hide_in_task_list inverted semantics. On task update, the old hide_in_task_list is replaced by enable_visible_in_task_list — the polarity flips. A mechanical rename will silently invert your behaviour. Audit every call site by hand and delete the old option rather than relying on the alias.

enable_ask_user → interactive_mode. Straightforward rename, but the legacy alias is still accepted, so nothing fails loudly if you miss one. Grep for both and remove the old name; the alias will disappear in a future major.

agent_profile values changed. Use standard, lite, or max. The manus-1.6* constants still resolve as compatibility aliases, which means a wrong-but-aliased value produces no error. Prefer a PHP enum or a config constant over raw strings so invalid values fail at boot, not at runtime.

task.detail nesting. Responses come back wrapped. 2.1.0 normalises this, but if you have any pre-2.1.0 code doing $response['id'] it may now be reading through a different path. Add one assertion in your test suite that a created task yields a non-empty ID — it catches envelope regressions in a single line.

Uploads accept any 2xx. Fixed in this release; previously only 200/201 counted. If you had a workaround checking specific codes or parsing 204 as failure, remove it.

Timeouts and response size. Both are now validated and enforced. Long-running task payloads with large detail blobs will hit the response-size guard. Tune it in config/manus.php deliberately — don't just raise it to infinity, because a runaway response in a queue worker is a memory incident. Keep the timeout below your queue's retry_after.

OAuth token lifecycle is your problem. The SDK adds bearer auth but the notes say nothing about refresh. Build a token provider that refreshes before expiry (store expires_at, refresh with a margin) and inject the current token; don't hand a static token to a long-running worker.

Config caching. php artisan config:cache in deploy means .env changes to MANUS_* won't take effect until you re-cache. The classic symptom is "works locally, 401 in staging."

Laravel 8 support is a floor, not a target. CI covers PHP 8.2–8.4 only, so Laravel 8/9 on PHP 8.1 is claimed but untested by the matrix. On older stacks, pin the package and expect to own the bugs.

Debugging. Log the request payload and the raw response body around failures — the improved error handling returns structured API errors, but the original body is what tells you which field the API rejected. Never log the bearer token or API key; scrub Authorization in your logging middleware.

Extension point. If you need a resource the SDK doesn't wrap, drop to the underlying HTTP client from the container and reuse its auth and timeout config rather than creating a second Guzzle client — otherwise you get two different retry/timeout behaviours in one app.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels