YouBothAgent▾
You — Business rules and flows you own. Read these yourself.
Both — Know the idea; your agent follows the details.
Agent — Conventions and references your agent follows. Look up as needed.
General▾
Interface▾
Observability▾
Scripts
A script is a TypeScript file that boots your app's server, does one job, and exits. Reach for it when the job should live in a file rather than at a prompt:
| Command | Form |
|---|---|
| ↳ Good for | |
akan script | A file in script/ you can review and rerun |
| Seed data, migrations, checks, small maintenance fixes | |
akan console | A prompt that is gone when you close it |
| Inspecting a service, trying a query, one small operator command | |
- Same wiring as the app. A script reuses the services, signals and adaptors the app already wires together.
- No traffic, no schedules. It opens no port and runs none of the app's init, interval, cron or queue jobs.
- Small and disposable. Keep one job per script, and delete it once the job is done.
Create And Run
Put the file directly in the app's
script/ folder, then pass its name to akan script.- Create
apps/koyo/script/hello.ts. The next section shows what goes in it. - From the workspace root, pass the app name and the file name. This runs
apps/koyo/script/hello.ts:
Terminal
Arguments
Both arguments may be left out, and the command then asks. They are positional, so the app comes first:
appStringoptional
The app name, needed with a file name. Left out, it asks from a list or uses the only app.
filenameStringoptional
A file directly in
script/; the .ts suffix is optional. Leave it out to pick from a list.- No subfolders. A name containing
/or..is refused, so keep every script at the top ofscript/. - It runs from the app folder. The working directory is
apps/koyo/, so relative paths start there.
Server Lifecycle
Every script has the same frame: start the server, do the job, and stop the server in
finally. The smallest script looks like this:apps/koyo/script/hello.ts
serveris the app's own. It comes fromapps/koyo/server.ts, so the script boots the same modules the app does.start()wires the app but opens no port. Underakan scriptit connects the databases and creates the adaptors, services and signals, and stops there.stop()belongs infinally. Even when the job throws, database connections, timers and adaptors are cleaned up.
Use Services
Do the work through services rather than direct database writes. A service already knows the domain rules, the database access and its other dependencies.
This script finishes every ice cream order still left in
served:apps/koyo/script/finishServedOrders.ts
server.get(srv.IcecreamOrderService)finds the service by its class, so every method is typed.listByStatusescomes from the model'sbyStatusesfilter. Every filter gives the service alist<Filter>like it.finishIcecreamOrderruns the same state check the app does, so an order that is notservedis refused.
Lookup Helpers
Once
server.start() resolves, server hands out any service, signal or adaptor the app registered. Prefer a class to a name string, which is not type-checked.| Finds by | Call |
|---|---|
| ↳ What you get | |
| Class | server.get(srv.IcecreamOrderService) |
| A service, signal or adaptor instance, fully typed. | |
| Role | server.get(StorageAdaptorRole) |
| The storage adaptor the app actually uses, whatever its implementation. | |
| refName | server.getService("icecreamOrder") |
| A service. | |
| refName | server.getSignal("icecreamOrder") |
| A signal, when the script should run signal logic. | |
| refName | server.getAdaptor("blobStorage") |
| An adaptor, for infrastructure work. | |
- A refName is the name a module registers under. For a service or signal it is the camelCase module name, such as
icecreamOrder; for an adaptor it is the key passed toadapt(). - A lib's classes sit under the lib's name, as in
srv.shared.UserService. - Import
StorageAdaptorRolefromakanjs/service. A role finds the adaptor even after the app swaps in its own implementation.


Look up only after start. Until
await server.start() resolves, every lookup throws.Change Data Safely
A script that changes data should show what it is about to do before it does it. Three habits cover most of it:
- Print the target environment first.
getEnv().environmentnames the environment the script is about to change. - Make a dry run the default. It only shows what would change. Read an env var such as
APPLY=1, and change nothing without it. - Write through service methods. The domain rules then stay in one place instead of being copied into the script.
Here is the script from above with the first two habits added:
apps/koyo/script/finishServedOrders.ts
Run it once to read the count, then again to apply it:
Terminal
AKAN_PUBLIC_ENVpicks the environment. The server reads the matchingenv/env.server.<env>.ts, and a new workspace's.envsets it tolocal.- A
returninsidetrystill reachesfinally, so the dry run stops the server too.


Nothing after the file name reaches the script.
akan script takes only the app and the file name, and an extra argument or unknown flag is an error. Environment variables do reach the script, so pass a flag as one.