The repository includes the Phase 0–6 product foundation: authenticated application and workflow catalogs, admin management, versioned compute billing, PostgreSQL-backed task execution, temporary file streaming, the RunningHub adapter, dynamic single/batch workspaces, persistent mixed application/workflow tabs, composed workflow execution, user-scoped history, and expiry cleanup.
- Node.js 22.18.0 and npm 10.9.3 (pinned in
.nvmrcandpackage.json) - Docker with Compose
cp .env.example .env.local
npm install
docker compose up -d postgres
DATABASE_URL=postgres://ai_factory:ai_factory@localhost:5432/ai_factory npm run db:migrate
npm run devThe development command starts both the Next.js web server and the persistent PostgreSQL queue worker. Task submission only reserves balance and enqueues a task; the worker performs the RunningHub submission and result polling.
If port 5432 is already occupied, set POSTGRES_PORT and use the same port in DATABASE_URL.
The initial administrator is created only when all three seed variables are supplied. The password is Argon2id-hashed and is never logged.
DATABASE_URL=postgres://ai_factory:ai_factory@localhost:5432/ai_factory \
SEED_ADMIN_USERNAME=admin \
SEED_ADMIN_PASSWORD='replace-with-a-long-local-password' \
npm run db:seednpm run typecheck
npm test
npm run build
npm run test:e2eFor database integration and seed verification, start the isolated test database and explicitly pass its URL:
docker compose --profile test up -d postgres-test
TEST_DATABASE_URL=postgres://ai_factory_test:ai_factory_test@localhost:5433/ai_factory_test npm run db:migrate:test
TEST_DATABASE_URL=postgres://ai_factory_test:ai_factory_test@localhost:5433/ai_factory_test npm run db:seed:test
TEST_DATABASE_URL=postgres://ai_factory_test:ai_factory_test@localhost:5433/ai_factory_test npm run test:dbProvider and storage tests use contracts/fakes only and never read production credentials. See work/docs/adr/0001-external-adapters.md for the boundary and documentation rule.
All point amounts are persisted as integer milli-points (1 point = 1,000 milli-points). Task submission, reservation, ledger writes, settlement, and release run in PostgreSQL transactions with idempotency keys.
# Scheduler: grant the active rule's daily amount once per Asia/Shanghai day
DATABASE_URL=postgres://ai_factory:ai_factory@localhost:5432/ai_factory npm run grants:daily
# Inspect eligible users without writing
DATABASE_URL=postgres://ai_factory:ai_factory@localhost:5432/ai_factory npm run grants:daily -- --dry-run
# Explicit admin adjustment
DATABASE_URL=postgres://ai_factory:ai_factory@localhost:5432/ai_factory npm run balance:add -- \
--user target-user --points 10.5 --note "support adjustment" \
--actor admin --request-id support-20260806-001
# PostgreSQL queue worker for production; it stays alive and multiple processes are safe
DATABASE_URL=postgres://ai_factory:ai_factory@localhost:5432/ai_factory npm run workerRunningHub integration follows the current official Chinese API documentation summarized in work/docs/runninghub-integration-v2.md. Application discovery reads editable nodes and the first HTTPS cover from /api/webapp/apiCallDemo; uploads use /openapi/v2/media/upload/binary, AI App submission uses /task/openapi/ai-app/run, and polling uses /openapi/v2/query. RUNNINGHUB_API_KEY remains a global server-side secret and each application's WebApp ID is stored in admin configuration. A positive verified RH coin charge settles at the application's configured reserved price, zero coins releases the reservation, and a result without verified usage remains pending_settlement.
RUNNINGHUB_API_KEY=... DATABASE_URL=... npm run provider:smoke -- runninghub --application-id <application-uuid>
# Real file upload + task execution smoke test
RUNNINGHUB_API_KEY=... DATABASE_URL=... npm run provider:file-smoke -- \
--application-id <application-uuid> --source-url <https-image-url>For the confirmed MVP temporary-file mode, clients upload a raw request body to /api/uploads with applicationId, fieldKey, and fileName query parameters plus Content-Type and X-File-Size headers. The Web service validates ownership, schema, MIME type, extension, and size, then streams the body once to RunningHub without writing it to local disk. RunningHub files are limited to 30 MB and treated as expiring after approximately 24 hours.
const response = await fetch(
`/api/uploads?applicationId=${applicationId}&fieldKey=${fieldKey}&fileName=${encodeURIComponent(file.name)}`,
{
method: "POST",
headers: { "content-type": file.type, "x-file-size": String(file.size) },
body: file,
},
);
const { intent } = await response.json();
// Submit intent.id as the corresponding file field value.COZE is intentionally not implemented yet. OSS/COS is deferred; the legacy StorageAdapter path remains fail-closed. Temporary RunningHub references cannot provide seven-day durability or user download authorization for original inputs.
Online applications open at /apps/[appId]. Input and output controls are generated from the latest published schema; provider bindings and raw provider payloads remain server-side. Each browser keeps independent drafts, upload references, and task associations for up to 12 application tabs. Polling uses bounded exponential backoff and pauses while the page is hidden.
Task history is available at /history (/tasks redirects there). Search, status filtering, pagination, ownership checks, result visibility, and safe failure messages are enforced on the server.
Run expiry cleanup from the scheduler. Dry-run reports affected task results and temporary upload/artifact references without writing:
DATABASE_URL=postgres://ai_factory:ai_factory@localhost:5432/ai_factory npm run cleanup:expired -- --dry-run
DATABASE_URL=postgres://ai_factory:ai_factory@localhost:5432/ai_factory npm run cleanup:expiredExpired output is hidden immediately by application APIs based on the exact expires_at timestamp. Cleanup later clears local temporary references and marks the task expired, while retaining the task input snapshot and immutable billing ledger for audit.
Every online AI application supports separate single and batch drafts. A batch contains up to 20 independently validated task groups with one shared batch ID; valid rows can be submitted while invalid rows stay expanded for correction. Each task reserves and settles compute independently, and failed tasks release their complete reservation.
Administrators manage composed workflows at /admin/workflows. A workflow can repeat an application, rename and reorder nodes, expose manual and/or automatic execution, define overall input fields, and map node inputs from overall inputs, earlier node outputs, or fixed values. Automatic workflows cannot go online until the latest ordered protocol hash has passed the dry-run protocol test.
Users discover online workflows before applications on category pages and run them at /workflows/[workflowId]. Manual mode keeps every node unlocked and independent. Automatic mode accepts only overall inputs, executes nodes sequentially through the existing worker, stops without retry after the first failure, and marks remaining nodes as skipped. Definitions, node names, inputs, outputs, status, batch position, and billing are snapshotted so historical runs remain readable after later configuration changes or soft deletion.