@akanjs/cli 3.0.1 → 3.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.build-stamp +1 -1
- package/{CodeAgent-zfab7pkn.js → CodeAgent-szbjv4gv.js} +2 -2
- package/{CodeTui-0zhnr0pw.js → CodeTui-5nw86ab6.js} +1 -1
- package/{agent.command-aja6z9mn.js → agent.command-np1dffkb.js} +5 -5
- package/akanCode.js +2 -2
- package/{application.command-049ehgx3.js → application.command-gk69v1xs.js} +8 -8
- package/{applicationBuildRunner-75n5rpwp.js → applicationBuildRunner-wx09n9g6.js} +3 -3
- package/buildBatch.proc.js +4 -4
- package/{cloud.command-gw4kqxt8.js → cloud.command-tw06613r.js} +10 -10
- package/cloud.script-64gzba8r.js +20 -0
- package/{code.command-x9t6z5nw.js → code.command-hmtx14b2.js} +6 -6
- package/{context.command-yfbe1rtd.js → context.command-6aqvdx43.js} +13 -13
- package/context.runner-hnkvv3z3.js +21 -0
- package/{guideline.command-kq9gp690.js → guideline.command-axvf68gc.js} +2 -2
- package/guidelines/conventions/conventions.instruction.md +89 -17
- package/guidelines/queryRule/queryRule.instruction.md +4 -2
- package/guidelines/runtimeRule/runtimeRule.instruction.md +10 -5
- package/guidelines/transportRule/transportRule.instruction.md +40 -4
- package/guidelines/workspaceOnboarding/workspaceOnboarding.instruction.md +1 -1
- package/incrementalBuilder.proc.js +3 -3
- package/{index-66yspb67.js → index-1bjkfvm9.js} +29 -27
- package/{index-b95yhqjr.js → index-1gs9g12n.js} +3 -3
- package/{index-y7hx1ygz.js → index-3xmzr1p1.js} +2 -2
- package/{index-mzjb6zp4.js → index-50jjwg8y.js} +1 -1
- package/{index-150s07n8.js → index-50wb2a26.js} +313 -93
- package/{index-8b639zes.js → index-6keyf4sg.js} +4 -4
- package/{index-vs8mefhy.js → index-bsv901fd.js} +7 -7
- package/{index-5pvxf7z0.js → index-cb6yc5mb.js} +2 -2
- package/{index-rfcz04w9.js → index-f4e08ptp.js} +4 -4
- package/{index-25fvrxmy.js → index-g38nhv87.js} +1 -1
- package/{index-m3xsb1ms.js → index-gtjgnv36.js} +29 -11
- package/{index-mb59b8x0.js → index-h9ht6s60.js} +1 -1
- package/{index-5g4rkrvt.js → index-hp3stk1m.js} +3 -3
- package/{index-h54yj9wr.js → index-hxnnfy57.js} +6 -5
- package/{index-kn5d4jmg.js → index-j38qkvkk.js} +7 -7
- package/{index-y5ga5w9t.js → index-jb0hmt3k.js} +3 -3
- package/{index-7p4c0yer.js → index-jk9xnck6.js} +33 -11
- package/{index-senscydg.js → index-k9rrten6.js} +6 -1
- package/{index-qe71g4ze.js → index-nwd6qzvv.js} +42 -23
- package/{index-a37by9hn.js → index-q6ft9fkc.js} +4 -4
- package/{index-br8pkrk1.js → index-t91a7339.js} +5 -5
- package/{index-c14nsvnv.js → index-xwprn9nr.js} +1 -1
- package/{index-fkvb79qe.js → index-z750gk1j.js} +15 -11
- package/{index-5q14pwzd.js → index-zwd84m0w.js} +4 -4
- package/{index-8kvy8qb4.js → index-zyw622y1.js} +12 -2
- package/index.js +21 -21
- package/{library.command-keqjevpp.js → library.command-9m2rdwwz.js} +3 -3
- package/{localRegistry.command-pydeq5zf.js → localRegistry.command-kda81jyk.js} +9 -9
- package/{module.command-dkrnqx3v.js → module.command-q63dffya.js} +5 -5
- package/{package.command-wmc0ys5y.js → package.command-y2jh0m99.js} +3 -3
- package/package.json +2 -2
- package/{page.command-tf3erhrv.js → page.command-f3e4cwhh.js} +3 -3
- package/{primitive.command-7hqpegj4.js → primitive.command-r4kq75qj.js} +6 -6
- package/{quality.command-8122axq3.js → quality.command-f7gzh26z.js} +9 -2
- package/{repair.command-mcy12h0n.js → repair.command-0qmyr6qa.js} +5 -5
- package/{scalar.command-vvvbzttc.js → scalar.command-kddn7av4.js} +4 -4
- package/{subspace.command-pzvqfy5h.js → subspace.command-ft65qvc4.js} +5 -7
- package/templates/app/tsconfig.json.template +1 -6
- package/templates/app/ui/Recipe/jellyButton.ts +1 -0
- package/templates/facetIndex/index.ts +21 -6
- package/templates/lib/dict.ts +9 -1
- package/templates/lib/srv.ts +2 -3
- package/templates/libRoot/common/index.ts +2 -7
- package/templates/libRoot/srvkit/index.ts +2 -7
- package/templates/libRoot/ui/index.ts +2 -7
- package/templates/libRoot/webkit/index.ts +2 -7
- package/templates/moduleRoot/index.tsx +3 -3
- package/{tunnel.command-82zz5xnq.js → tunnel.command-b3ymdk4f.js} +2 -2
- package/{workflow.command-y8x3kgjv.js → workflow.command-z1srcdfw.js} +10 -10
- package/{workspace.command-jvsb7brc.js → workspace.command-vp6vmx3z.js} +21 -21
- package/cloud.script-c723cpcb.js +0 -20
- package/context.runner-erfe68j5.js +0 -21
- package/index-bjwxs904.js +0 -77
- package/templates/libRoot/common/commonLogic.ts +0 -14
- package/templates/libRoot/srvkit/backendLogic.ts +0 -13
- package/templates/libRoot/webkit/frontendLogic.ts +0 -13
|
@@ -8,7 +8,8 @@ mutation is mounted under. `conventions` carries the invariants — this is the
|
|
|
8
8
|
|
|
9
9
|
- Guards run on both HTTP and websocket calls. Read the caller with `context.get<T>("account")` (`pkgs/akanjs/signal/signalContext.ts`) instead of branching on `getHttpContext()` / `getWebSocketContext()`.
|
|
10
10
|
- Slice-level `guards` only reach the generated query/mutation endpoints. A `pubsub`/`message` endpoint is unguarded unless it declares its own `guards` in its signal option.
|
|
11
|
-
- A pubsub room is authorized once, at subscribe. When a socket's credential changes the framework re-runs each room's guards and unsubscribes the ones that now fail (`SignalResolver.revalidateWsRooms`), so guards must stay side-effect free and safe to re-run.
|
|
11
|
+
- A pubsub room is authorized once, at subscribe. When a socket's credential changes the framework re-runs each room's guards and unsubscribes the ones that now fail (`SignalResolver.revalidateWsRooms`), so guards must stay side-effect free and safe to re-run. Each re-check runs on a fresh `SignalContext`, so a guard that memoizes per context answers anew.
|
|
12
|
+
- **Arguments are parsed after the `"account"` guards** (`SignalContext.init` reads the body, `exec` parses): the order is middlewares → `account` guards → parse → `resource` guards → internal arguments → handler, whatever order `guards: [...]` lists them in. A caller an account guard refuses never reaches a scalar parser, which is what keeps a slow parser from being a pre-auth denial of service. A guard or middleware that reads `context.args` / `getArg()` earlier still gets the value, parsed on first read — a mismarked account guard keeps working. One visible consequence: an unauthenticated call with a malformed argument answers 401/403 instead of 400.
|
|
12
13
|
- A websocket carries its credential in the handshake snapshot on `ws.data` (`AppWsData`); clients that hold the token in memory send it with `fetch.setJwt(...)`, which forwards an auth frame over the socket.
|
|
13
14
|
- **Never read the caller's IP off the socket or the request peer — take `.with(Ip)`.** Whenever a federation
|
|
14
15
|
gateway is in front, `ws.remoteAddress` and the child's own peer are the *gateway* (`127.0.0.1`) for every
|
|
@@ -74,11 +75,46 @@ mutation is mounted under. `conventions` carries the invariants — this is the
|
|
|
74
75
|
never cached, and a relation still costs its own load. The cache saves the handler, not the serialization.
|
|
75
76
|
- **A cache backend that is down does not take the endpoint down.** A failed read is warned and the call runs
|
|
76
77
|
uncached; a failed write is warned and dropped.
|
|
77
|
-
- The default middleware chain is `Logging → Timeout → <lib middlewares>`, and the call inside it runs
|
|
78
|
-
|
|
79
|
-
stands in for. **`Retry` was removed** — an automatic re-run of an endpoint
|
|
78
|
+
- The default middleware chain is `Logging → Timeout → <lib middlewares>`, and the call inside it runs an
|
|
79
|
+
`account` rate limit → account guards → argument parse → resource guards → internal arguments → cache →
|
|
80
|
+
handler: a hung cache backend is bounded by the same deadline as the handler it stands in for. **`Retry` was removed** — an automatic re-run of an endpoint
|
|
80
81
|
whose failure it cannot classify replays whatever the first attempt already did.
|
|
81
82
|
|
|
83
|
+
## Rate Limits
|
|
84
|
+
|
|
85
|
+
- **`{ rateLimit: { calls, windowMs?, by? } }` in an endpoint's signal option** counts calls per caller per window
|
|
86
|
+
(`windowMs` 60 000 unless named). `by: "ip"` (the default) is checked in `SignalContext.init`, after the
|
|
87
|
+
cross-site check and before the body is read, so a refused flood costs no parse at all. `by: "account"` is
|
|
88
|
+
checked after the middlewares and before the guards, keyed by `accountKey(account)`; a caller it names nothing
|
|
89
|
+
for — an anonymous one — is counted by address. Leaving a pubsub room is never counted.
|
|
90
|
+
- **The answer is 429 `base.error.tooManyRequests` with `data: { seconds }`** and a `Retry-After` header; a WS
|
|
91
|
+
call gets the same payload as its error frame, and the HTTP client restores a proxy's own non-JSON 429 under the
|
|
92
|
+
same key (seconds from its `Retry-After`, else 60). The first refusal per endpoint per process is warned once;
|
|
93
|
+
later ones log at `debug`, so a flood does not become a log flood.
|
|
94
|
+
- **Keys and addresses.** The address is `context.getClientIp()` — the trusted-proxy answer, never a raw header
|
|
95
|
+
from an untrusted peer. Callers whose address is unknown share one `unknown` budget (fail closed), and the first
|
|
96
|
+
such call is warned about, since it usually means the proxy in front records no `X-Real-IP`/`X-Forwarded-For`
|
|
97
|
+
or is not trusted (`AKAN_TRUSTED_PROXIES`). A page's server-side fetches carry the visitor's address: the page
|
|
98
|
+
renderer hands the RSC worker the address it resolved as `x-real-ip`, and the server-side fetch client forwards
|
|
99
|
+
it on same-origin calls.
|
|
100
|
+
- **Counters live in the cache** (`CacheAdaptor.incr`, epoch-aligned windows), so replicas draw on one budget; a
|
|
101
|
+
cache that fails falls back to counting per process, warned once. The MCP endpoint's own limiter is the same
|
|
102
|
+
`RateLimiter` under its own topic, and keys an anonymous MCP caller by the same trusted address.
|
|
103
|
+
- **App and lib configuration** is `option.setRateLimit(...)` in `lib/option.ts`, merged in mount order with the
|
|
104
|
+
app last: `budget` is the default for every endpoint that declares none (unset leaves those unlimited),
|
|
105
|
+
`endpoints` overrides one by key — a lib's declared budget included — or exempts it with `false`, `accountKey`
|
|
106
|
+
names who `by: "account"` counts (default: `account.id ?? sub ?? sid`; a lib whose accounts carry `self`/`me`
|
|
107
|
+
should name `self.id ?? me.id`, or each session counts apart), and `enabled: false` (or `setRateLimit(false)`)
|
|
108
|
+
turns budgets off for a deployment rate-limited at its edge. `rateLimit: false` on an endpoint exempts it from
|
|
109
|
+
the app's `budget` only.
|
|
110
|
+
- **`AKAN_RATE_LIMIT`**: `off` turns every budget off whatever the code says; `on` turns them on in a `local`
|
|
111
|
+
environment, where they are otherwise off; `<calls>[/<seconds>]` is the default budget wherever the code names
|
|
112
|
+
none. Tests run in the `testing` environment, where budgets apply.
|
|
113
|
+
- **A business key is a service's own count**: `rateLimit: plug(RateLimit)` then
|
|
114
|
+
``await this.rateLimit.consume(`otp:${phone}`, { calls: 5, windowMs: 3_600_000 })``, which answers
|
|
115
|
+
`{ ok: true } | { ok: false, retryAfterMs }` and is never switched off — throw the module's own `Err` with the
|
|
116
|
+
wait when it refuses.
|
|
117
|
+
|
|
82
118
|
## Binary Pubsub
|
|
83
119
|
|
|
84
120
|
- **`pubsub(Binary)` sends its payload in a websocket binary frame**, skipping the JSON `{ type: "pub" }`
|
|
@@ -124,7 +124,7 @@ starts it itself. The full contract — targets, updates, what the carried serve
|
|
|
124
124
|
```bash
|
|
125
125
|
akan start-desktop <%= appName %> # Run the desktop app on this computer
|
|
126
126
|
akan start-ios <%= appName %> # Run it in a simulator or on an iPhone (start-android alike)
|
|
127
|
-
akan build-desktop <%= appName %> # Build it for this computer into
|
|
127
|
+
akan build-desktop <%= appName %> # Build it for this computer into dist/native/<%= appName %>/<target>/build
|
|
128
128
|
akan build-desktop <%= appName %> --installer true # Windows: add a per-user setup program
|
|
129
129
|
akan update-keygen <%= appName %> # Make the update signing key once; print its public key
|
|
130
130
|
akan publish-update <%= appName %> # Build and sign a desktop release to upload to updates.url
|
|
@@ -3,12 +3,12 @@ import {
|
|
|
3
3
|
ApplicationBuildReporter,
|
|
4
4
|
HmrWatcher,
|
|
5
5
|
WatchRootResolver
|
|
6
|
-
} from "./index-
|
|
6
|
+
} from "./index-k9rrten6.js";
|
|
7
7
|
import {
|
|
8
8
|
CodegenLock,
|
|
9
9
|
WorkspaceExecutor,
|
|
10
10
|
AppExecutor
|
|
11
|
-
} from "./index-
|
|
11
|
+
} from "./index-50wb2a26.js";
|
|
12
12
|
import"./index-52w0fvh8.js";
|
|
13
13
|
import {
|
|
14
14
|
GraphClientEntryDiscovery,
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
DevChangePlanner,
|
|
21
21
|
DevGeneratedIndexSync,
|
|
22
22
|
SsrDevBundler
|
|
23
|
-
} from "./index-
|
|
23
|
+
} from "./index-gtjgnv36.js";
|
|
24
24
|
import {
|
|
25
25
|
prepareDevWatchBatch
|
|
26
26
|
} from "./index-a5vmm6x4.js";
|
|
@@ -2,14 +2,12 @@
|
|
|
2
2
|
import {
|
|
3
3
|
runner,
|
|
4
4
|
script
|
|
5
|
-
} from "./index-
|
|
6
|
-
import {
|
|
7
|
-
LibExecutor
|
|
8
|
-
} from "./index-150s07n8.js";
|
|
5
|
+
} from "./index-q6ft9fkc.js";
|
|
9
6
|
import {
|
|
10
7
|
LibSource,
|
|
11
|
-
formatLibStatuses
|
|
12
|
-
|
|
8
|
+
formatLibStatuses,
|
|
9
|
+
LibExecutor
|
|
10
|
+
} from "./index-50wb2a26.js";
|
|
13
11
|
|
|
14
12
|
// pkgs/@akanjs/cli/library/library.script.ts
|
|
15
13
|
import { Logger } from "akanjs/common";
|
|
@@ -86,28 +84,32 @@ class LibraryRunner extends runner("library") {
|
|
|
86
84
|
return await Promise.all(libNames.map((libName) => new LibSource(LibExecutor.from(workspace, libName)).status()));
|
|
87
85
|
}
|
|
88
86
|
async mergeLibraryDependencies(lib) {
|
|
89
|
-
const libPackageJson = await lib.getPackageJson();
|
|
90
|
-
const
|
|
91
|
-
const
|
|
92
|
-
const
|
|
93
|
-
const
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const rootVersion = rootDependencies[dep] ?? "0.0.0";
|
|
98
|
-
const newerVersion = compareSemver(rootVersion, libVersion) > 0 ? rootVersion : libVersion;
|
|
99
|
-
return [dep, newerVersion];
|
|
100
|
-
}));
|
|
101
|
-
Object.keys(allDependencies).sort().forEach((dep) => {
|
|
102
|
-
if (libPackageJson.dependencies?.[dep] || rootPackageJson.dependencies?.[dep])
|
|
103
|
-
dependencies[dep] = allDependencies[dep];
|
|
104
|
-
else
|
|
105
|
-
devDependencies[dep] = allDependencies[dep];
|
|
87
|
+
const [libPackageJson, rootPackageJson] = await Promise.all([lib.getPackageJson(), lib.workspace.getPackageJson()]);
|
|
88
|
+
const rootVersionOf = (dep) => rootPackageJson.dependencies?.[dep] ?? rootPackageJson.devDependencies?.[dep];
|
|
89
|
+
const missingFrom = (deps = {}) => Object.entries(deps).filter(([dep]) => !rootVersionOf(dep));
|
|
90
|
+
const dependencies = missingFrom(libPackageJson.dependencies);
|
|
91
|
+
const devDependencies = missingFrom(libPackageJson.devDependencies).filter(([dep]) => !libPackageJson.dependencies?.[dep]);
|
|
92
|
+
const behind = Object.entries({ ...libPackageJson.devDependencies, ...libPackageJson.dependencies }).flatMap(([dep, version]) => {
|
|
93
|
+
const rootVersion = rootVersionOf(dep);
|
|
94
|
+
return rootVersion && compareSemver(version, rootVersion) > 0 ? [`${dep} ${rootVersion} (lib: ${version})`] : [];
|
|
106
95
|
});
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
96
|
+
if (behind.length)
|
|
97
|
+
lib.logger.warn(`Kept the root versions of packages libs/${lib.name} lists newer: ${behind.join(", ")}`);
|
|
98
|
+
if (dependencies.length || devDependencies.length) {
|
|
99
|
+
await lib.workspace.setPackageJson({
|
|
100
|
+
...rootPackageJson,
|
|
101
|
+
dependencies: this.#withAdded(rootPackageJson.dependencies, dependencies),
|
|
102
|
+
devDependencies: this.#withAdded(rootPackageJson.devDependencies, devDependencies)
|
|
103
|
+
});
|
|
104
|
+
await lib.workspace.spawn("bun", ["install"]);
|
|
105
|
+
}
|
|
106
|
+
if (await lib.workspace.hasChanges())
|
|
107
|
+
await lib.workspace.commit(`Merge ${lib.name} library dependencies`);
|
|
108
|
+
}
|
|
109
|
+
#withAdded(deps, added) {
|
|
110
|
+
if (!added.length)
|
|
111
|
+
return deps;
|
|
112
|
+
return Object.fromEntries([...Object.entries(deps ?? {}), ...added].sort(([a], [b]) => a < b ? -1 : 1));
|
|
111
113
|
}
|
|
112
114
|
}
|
|
113
115
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
runner,
|
|
4
4
|
script
|
|
5
|
-
} from "./index-
|
|
5
|
+
} from "./index-q6ft9fkc.js";
|
|
6
6
|
import {
|
|
7
7
|
AGENT_BLOCK_START,
|
|
8
8
|
AGENT_BLOCK_END,
|
|
@@ -13,14 +13,14 @@ import {
|
|
|
13
13
|
CLAUDE_COMMENT_RULE,
|
|
14
14
|
AppExecutor,
|
|
15
15
|
LibExecutor
|
|
16
|
-
} from "./index-
|
|
16
|
+
} from "./index-50wb2a26.js";
|
|
17
17
|
import {
|
|
18
18
|
collectRecipeSources2,
|
|
19
19
|
scanRecipes2
|
|
20
20
|
} from "./index-hj6w4ka1.js";
|
|
21
21
|
import {
|
|
22
22
|
AkanContextAnalyzer
|
|
23
|
-
} from "./index-
|
|
23
|
+
} from "./index-zyw622y1.js";
|
|
24
24
|
import {
|
|
25
25
|
Prompter2
|
|
26
26
|
} from "./index-zzvvc845.js";
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
import {
|
|
3
3
|
runner,
|
|
4
4
|
script
|
|
5
|
-
} from "./index-
|
|
5
|
+
} from "./index-q6ft9fkc.js";
|
|
6
6
|
import {
|
|
7
7
|
FileSys2
|
|
8
8
|
} from "./index-52w0fvh8.js";
|
|
9
9
|
import {
|
|
10
10
|
PkgExecutor
|
|
11
|
-
} from "./index-
|
|
11
|
+
} from "./index-50wb2a26.js";
|
|
12
12
|
import {
|
|
13
13
|
TypeScriptDependencyScanner2
|
|
14
14
|
} from "./index-2ahj896a.js";
|