@akanjs/cli 3.0.0-beta.2 → 3.0.0-beta.21
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-q2m47rqp.js +9 -0
- package/CodeAgentRpcHost-p7h6n2hp.js +7 -0
- package/CodeAgentRpcListener-7p7z5dd7.js +97 -0
- package/CodeTui-pdqhc3sn.js +1457 -0
- package/README.md +3 -0
- package/TunnelShare-scf4e4pd.js +9 -0
- package/{agent.command-d2gfxfck.js → agent.command-ttn09rx3.js} +8 -9
- package/akanCode.js +102 -0
- package/application.command-7gweenv7.js +179 -0
- package/{applicationBuildRunner-kxwbssgn.js → applicationBuildRunner-38exmejd.js} +63 -64
- package/buildBatch.proc.js +26 -25
- package/cloud.command-bhztw7c2.js +95 -0
- package/cloud.script-aabyhgba.js +20 -0
- package/code.command-6s8c0k91.js +154 -0
- package/commandManifest.json +1 -1
- package/consoleToStderr-h5bryj94.js +7 -0
- package/context.command-phmgywt2.js +48 -0
- package/context.runner-yvyw5g2d.js +21 -0
- package/{dependencyScanner-shs82ynp.js → dependencyScanner-ye2e6pdz.js} +2 -2
- package/{devTui-40ggmd99.js → devTui-q7k7bakd.js} +122 -40
- package/fileSys-5ngz93ga.js +9 -0
- package/guideline.command-7jk47fgq.js +42 -0
- package/guidelines/agentRule/agentRule.instruction.md +209 -28
- package/guidelines/conventions/conventions.instruction.md +96 -29
- package/guidelines/fieldRule/fieldRule.instruction.md +1 -0
- package/guidelines/mcpRule/mcpRule.instruction.md +7 -5
- package/guidelines/queryRule/queryRule.instruction.md +17 -7
- package/guidelines/runtimeRule/runtimeRule.instruction.md +77 -3
- package/guidelines/ssrRule/ssrRule.instruction.md +4 -2
- package/guidelines/transportRule/transportRule.instruction.md +51 -0
- package/guidelines/workspaceRecipes/workspaceRecipes.instruction.md +66 -3
- package/incrementalBuilder.proc.js +31 -48
- package/{index-h7yw2z9n.js → index-0bq14vr1.js} +8 -14
- package/{index-259dsnz3.js → index-1db2cw0y.js} +5 -8
- package/index-22d57v4c.js +266 -0
- package/index-3hx1z96c.js +87 -0
- package/{index-kcxmv48c.js → index-3t7g9e4r.js} +45 -79
- package/{index-ne2qy0nk.js → index-4dn1p115.js} +12 -12
- package/{index-q0y2jq9j.js → index-5b6tavv3.js} +6 -3
- package/index-7cqjvraf.js +69 -0
- package/index-7md2g9vw.js +6 -0
- package/{index-zry5nz81.js → index-8kb5p54p.js} +51 -30
- package/{index-ktb5njgy.js → index-9629w7ns.js} +49 -99
- package/index-9dp031by.js +12 -0
- package/{index-a64esmtb.js → index-aadpbnt2.js} +172 -308
- package/{index-3d9y066c.js → index-bjwxs904.js} +9 -8
- package/index-c6eggbee.js +123 -0
- package/index-d0arnrm8.js +700 -0
- package/index-d2242ksy.js +99 -0
- package/{index-600zhnve.js → index-dg97qp24.js} +70 -200
- package/{index-92zztqy1.js → index-dy0jx9ay.js} +2 -17
- package/{index-w8kmx1b6.js → index-fgq75haa.js} +198 -290
- package/{index-2tv3fdx4.js → index-ga6hygxr.js} +944 -1333
- package/{index-by5gpp8x.js → index-hj6w4ka1.js} +2 -1
- package/index-hr5n7t4x.js +465 -0
- package/{index-hwkygn9s.js → index-khpfpcsd.js} +302 -33
- package/{index-7737gxcp.js → index-m0ms3n1c.js} +107 -241
- package/{index-ws5e0j51.js → index-mgfx3mfs.js} +43 -26
- package/index-p6pbz9n5.js +25 -0
- package/index-pp54p5zk.js +53 -0
- package/{index-j9yhg404.js → index-q44jbw48.js} +4 -7
- package/{index-mak3cd28.js → index-rmcw8yc8.js} +137 -255
- package/{index-pnqbkf5p.js → index-rnt66vym.js} +6 -2
- package/index-smme4r47.js +49 -0
- package/index-tgjy8xf3.js +187 -0
- package/index-v5eayqqw.js +2729 -0
- package/index-wa5jbxj1.js +760 -0
- package/{index-4edzf1gr.js → index-wp43xrjv.js} +77 -150
- package/{index-fz5q3gd7.js → index-ya5qbmyg.js} +10 -17
- package/index-yjkte9aj.js +1119 -0
- package/{index-gyd220p2.js → index-yy4s1j72.js} +554 -577
- package/{index-v0qh106m.js → index-z50k041j.js} +580 -862
- package/index.js +23 -22
- package/{library.command-nbsky672.js → library.command-ccb1cp92.js} +4 -5
- package/{localRegistry.command-yeq57wjr.js → localRegistry.command-ajb9n87j.js} +23 -42
- package/{module.command-29vqzrh8.js → module.command-5k6h407d.js} +8 -11
- package/{package.command-axv0abmn.js → package.command-e6ntvpez.js} +6 -7
- package/package.json +14 -6
- package/{page.command-vs9s3zrh.js → page.command-v4f90wb8.js} +4 -5
- package/{primitive.command-cbxmqbtc.js → primitive.command-g6km57q5.js} +9 -22
- package/prompter-ptaa3nk0.js +8 -0
- package/{quality.command-gmbs7nws.js → quality.command-de3p36pz.js} +205 -377
- package/{recipeScanner-fpagtgc1.js → recipeScanner-4b69ybe0.js} +1 -1
- package/{repair.command-bqsjptsj.js → repair.command-d3x1javq.js} +11 -35
- package/{routeSourceValidator-apyqw7wk.js → routeSourceValidator-7ycnfz0e.js} +0 -6
- package/{scalar.command-6k1vhe9s.js → scalar.command-7ezrac2b.js} +7 -10
- package/skills/akan-code/SKILL.md +130 -0
- package/skills/akan-debug/SKILL.md +42 -0
- package/skills/akan-deps/SKILL.md +50 -0
- package/skills/akan-err/SKILL.md +39 -0
- package/skills/akan-module/SKILL.md +45 -0
- package/skills/akan-scaffold/SKILL.md +76 -0
- package/skills/akan-scalar/SKILL.md +52 -0
- package/skills/akan-service/SKILL.md +34 -0
- package/skills/akan-store/SKILL.md +67 -0
- package/skills/akan-ui/SKILL.md +63 -0
- package/skills/akan-validate/SKILL.md +44 -0
- package/{subspace.command-fwjfcts7.js → subspace.command-ps71ynp6.js} +138 -67
- package/templates/__scalar/__model__/__model__.abstract.md.template +8 -0
- package/templates/appSample/lib/__scalar/workHistory/workHistory.abstract.ts +8 -39
- package/templates/appSample/lib/_noti/noti.abstract.ts +6 -29
- package/templates/appSample/lib/task/task.abstract.ts +12 -32
- package/templates/appSample/lib/task/task.signal.spec.ts +15 -18
- package/templates/appSample/lib/task/task.signal.test.ts +15 -1
- package/templates/appSample/lib/task/task.signal.ts +1 -1
- package/templates/appSample/srvkit/{AuthGuard.ts → guards.ts} +7 -4
- package/templates/appSample/srvkit/{SessionInternalArg.ts → internalArgs.ts} +6 -4
- package/templates/client.ts +7 -30
- package/templates/crudPages/[__model__Id]/edit/page.tsx +2 -1
- package/templates/crudPages/[__model__Id]/page.tsx +2 -1
- package/templates/crudPages/new/page.tsx +2 -1
- package/templates/crudPages/page.tsx +2 -1
- package/templates/crudSinglePage/page.tsx +2 -1
- package/templates/lib/__lib/lib.dictionary.ts +0 -2
- package/templates/lib/__lib/lib.document.ts +0 -2
- package/templates/lib/sig.ts +8 -30
- package/templates/localDev/docker-compose.yaml.template +3 -14
- package/templates/module/__model__.signal.ts +12 -4
- package/templates/pkgRoot/tsconfig.json.template +2 -7
- package/templates/server.ts +0 -2
- package/templates/service/__model__.abstract.md.template +8 -0
- package/templates/workspaceRoot/.gitignore.template +0 -1
- package/tunnel.command-hsrv2mny.js +129 -0
- package/{typeChecker-3xgrzc2v.js → typeChecker-px49mezc.js} +1 -1
- package/typecheck.proc.js +1 -1
- package/workflow.command-z2p32nmf.js +79 -0
- package/{workspace.command-8s050m0h.js → workspace.command-tmdqkrh0.js} +77 -109
- package/abstractCompactor-mjh5ppsq.js +0 -101
- package/application.command-pj2m7584.js +0 -204
- package/applicationReleasePackager-4yasxwv9.js +0 -244
- package/capacitorApp-36rbr858.js +0 -12
- package/cloud.command-jshwdq22.js +0 -93
- package/context.command-ez8a0zyz.js +0 -80
- package/devTuiView-c08ccbat.js +0 -9
- package/getDirname-dr3jz1xg.js +0 -7
- package/guideline.command-xmqwx8zm.js +0 -359
- package/guidelines/agentRule/agentRule.generate.json +0 -31
- package/guidelines/componentRule/componentRule.generate.json +0 -31
- package/guidelines/conventions/conventions.generate.json +0 -25
- package/guidelines/cssRule/cssRule.generate.json +0 -31
- package/guidelines/docPageRule/docPageRule.generate.json +0 -31
- package/guidelines/docPageRule/docPageRule.instruction.md +0 -25
- package/guidelines/docSyncRule/docSyncRule.generate.json +0 -31
- package/guidelines/docSyncRule/docSyncRule.instruction.md +0 -25
- package/guidelines/enumConstant/enumConstant.generate.json +0 -31
- package/guidelines/enumConstant/enumConstant.instruction.md +0 -28
- package/guidelines/fieldRule/fieldRule.generate.json +0 -31
- package/guidelines/framework/framework.generate.json +0 -32
- package/guidelines/mcpRule/mcpRule.generate.json +0 -35
- package/guidelines/modelConstant/modelConstant.generate.json +0 -32
- package/guidelines/modelDictionary/modelDictionary.generate.json +0 -32
- package/guidelines/modelDocument/modelDocument.generate.json +0 -31
- package/guidelines/modelService/modelService.generate.json +0 -31
- package/guidelines/modelSignal/modelSignal.generate.json +0 -31
- package/guidelines/modelStore/modelStore.generate.json +0 -31
- package/guidelines/modelTemplate/modelTemplate.generate.json +0 -31
- package/guidelines/modelUnit/modelUnit.generate.json +0 -31
- package/guidelines/modelUtil/modelUtil.generate.json +0 -31
- package/guidelines/modelView/modelView.generate.json +0 -31
- package/guidelines/modelZone/modelZone.generate.json +0 -31
- package/guidelines/moduleCodegen/moduleCodegen.generate.json +0 -31
- package/guidelines/moduleCodegen/moduleCodegen.instruction.md +0 -28
- package/guidelines/moduleOverview/moduleOverview.generate.json +0 -31
- package/guidelines/queryRule/queryRule.generate.json +0 -31
- package/guidelines/recipeRule/recipeRule.generate.json +0 -31
- package/guidelines/runtimeRule/runtimeRule.generate.json +0 -25
- package/guidelines/scalarConstant/scalarConstant.generate.json +0 -32
- package/guidelines/scalarDictionary/scalarDictionary.generate.json +0 -32
- package/guidelines/scalarModule/scalarModule.generate.json +0 -31
- package/guidelines/sharedUiUsage/sharedUiUsage.generate.json +0 -31
- package/guidelines/sharedUiUsage/sharedUiUsage.instruction.md +0 -25
- package/guidelines/ssrRule/ssrRule.generate.json +0 -31
- package/guidelines/transportRule/transportRule.generate.json +0 -31
- package/guidelines/utilUiUsage/utilUiUsage.generate.json +0 -31
- package/guidelines/utilUiUsage/utilUiUsage.instruction.md +0 -25
- package/guidelines/workspaceOnboarding/workspaceOnboarding.generate.json +0 -24
- package/guidelines/workspaceRecipes/workspaceRecipes.generate.json +0 -33
- package/index-2hq52jq3.js +0 -465
- package/index-47pj3aee.js +0 -76
- package/index-6wwzs1bc.js +0 -7
- package/index-8pm30m73.js +0 -841
- package/index-kn1h7p1x.js +0 -560
- package/index-meh558jy.js +0 -65
- package/index-rwg2aa4f.js +0 -229
- package/index-wt7qrtd4.js +0 -1516
- package/templates/__scalar/__model__/__model__.abstract.md +0 -28
- package/templates/module/__model__.abstract.md +0 -28
- package/templates/service/__model__.abstract.md +0 -28
- package/workflow.command-nwpcdc8p.js +0 -107
|
@@ -376,15 +376,16 @@ export default page()
|
|
|
376
376
|
|---|---|
|
|
377
377
|
| `interface PageProps { params }` + `[x]` folder | `.param("x", ID \| String \| Int \| …)` — one per `[x]` segment, every one of them, in path order |
|
|
378
378
|
| `searchParams.k` | `.search("k", Type)`; `[Type]` for a repeated key; the value arrives typed and optional |
|
|
379
|
-
| `params.lang` | `lang` — on every route's `.render()`, `.head()
|
|
379
|
+
| `params.lang` | `lang` — on every route's `.render()`, `.head()` and `.loading()` with no stage; `.param("lang")` is refused |
|
|
380
380
|
| `export const pageConfig = {…}` | `.config({…})` — `devOnly` stays a literal |
|
|
381
381
|
| `export const head = <…/>` / `generateHead({ params })` | `.head(<…/>)` / `.head(({ x }) => <…/>)` |
|
|
382
|
-
| `export const metadata` / `generateMetadata` | `.
|
|
382
|
+
| `export const metadata` / `generateMetadata` | `.head(<><title>…</title><meta name="description" content="…" /></>)` — there is no object form |
|
|
383
383
|
| `export function Loading()` | `.loading((args) => …)` |
|
|
384
384
|
| `export default async function Page({ params, searchParams })` | `.render(async ({ x, k }) => { …same body… })` — `async` only if it awaits |
|
|
385
385
|
| `_layout.tsx`: `export default function Layout({ children })` | `layout().render(({ children }) => …)` |
|
|
386
386
|
| `export function NotFound` / `export function Error` | `.notFound(fn)` / `.error(fn)` on `layout()` |
|
|
387
|
-
| root `_layout.tsx`: `fonts`, `theme`, `manifest`, `reconnect`, `wsConnect`, `layoutStyle
|
|
387
|
+
| root `_layout.tsx`: `fonts`, `theme`, `manifest`, `reconnect`, `wsConnect`, `layoutStyle` | `rootLayout().fonts([…]).theme("dark").manifest({…}).reconnect(false).wsConnect(false).layoutStyle("web")` |
|
|
388
|
+
| root `_layout.tsx`: `gaTrackingId` | none — render the app's own `"use client"` analytics component from the root layout's `.render()` |
|
|
388
389
|
| `prompt()` on `endpoint()` (removed) | `.prompt(name, description)` on the page that shows the same screen |
|
|
389
390
|
|
|
390
391
|
Rules the loader and `akan sync` enforce: a chain module has no other export; `page()` belongs in a page file and
|
|
@@ -393,6 +394,68 @@ Rules the loader and `akan sync` enforce: a chain module has no other export; `p
|
|
|
393
394
|
`_overrides.tsx` is untouched. MCP: `prompts/get` now runs the page's body under the caller's token and hands the
|
|
394
395
|
model the description, what the page fetched, and the tools of those modules — see `mcpRule`.
|
|
395
396
|
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
### Recipe 8: Migrating the LLM Adaptor and the Removed `--ai` Commands
|
|
400
|
+
|
|
401
|
+
Two changes land together. The runtime LLM seam now names a wire rather than a vendor, and the CLI's AI editor is
|
|
402
|
+
gone. They touch different files and neither one is optional — the first breaks a build or, worse, degrades
|
|
403
|
+
silently; the second breaks a script that calls a command that no longer exists.
|
|
404
|
+
|
|
405
|
+
#### The adaptor, in `lib/option.ts` and `env/env.server.*.ts`
|
|
406
|
+
|
|
407
|
+
`DeepseekLlm` is deleted. `OpenaiLlm` is the default and speaks the chat-completions dialect to whatever `host`
|
|
408
|
+
names, so DeepSeek, Groq, Together, OpenRouter, Ollama and a self-hosted vLLM are the same class with a different
|
|
409
|
+
`host` and `model`. `AnthropicLlm` is unchanged.
|
|
410
|
+
|
|
411
|
+
```ts
|
|
412
|
+
// before — env/env.server.local.ts
|
|
413
|
+
export const env: ModulesOptions = {
|
|
414
|
+
...libEnv,
|
|
415
|
+
llm: { apiKey: process.env.DEEPSEEK_KEY },
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
// after — the default model and host are gone, so name both
|
|
419
|
+
export const env: ModulesOptions = {
|
|
420
|
+
...libEnv,
|
|
421
|
+
llm: { apiKey: process.env.DEEPSEEK_KEY, model: "deepseek-v4-flash", host: "https://api.deepseek.com" },
|
|
422
|
+
};
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
| Before | After | What happens if you skip it |
|
|
426
|
+
|---|---|---|
|
|
427
|
+
| `setLlm({ apiKey })`, relying on the DeepSeek default | `setLlm({ apiKey, model, host })` | The app boots, the log warns once, and the chat answers `llmUnavailable` to every turn |
|
|
428
|
+
| `applyAdaptor(LlmAdaptorRole, DeepseekLlm)` | `OpenaiLlm` + `host: "https://api.deepseek.com"` | Build error — the class is gone |
|
|
429
|
+
| `OpenaiLlm` pointed at a **named** `host`, reading images | add `accepts: { image: true }` to `setLlm` | **Silent**: images reach the model as a note saying they could not be read |
|
|
430
|
+
| An adaptor of your own throwing `agent.error.<vendor>RequestFailed` | `new Err("agent.error.llmRequestFailed", { provider, status, reason })` | The chat prints the raw key instead of a sentence |
|
|
431
|
+
| Provider settings in a second `option.use({ myLlmOption })` | put them in `setLlm` and read `use<MyLlmOption>()` | Nothing breaks; the second channel is no longer needed |
|
|
432
|
+
|
|
433
|
+
`OpenaiLlm` claims vision for its default host (`https://api.openai.com/v1`) and for nothing else, because a host
|
|
434
|
+
you named is a gateway it knows nothing about — and bytes handed to a model that cannot decode them kill the whole
|
|
435
|
+
turn on a 400, where text-only degrades them to a note. Declare `accepts` whenever you set `host`.
|
|
436
|
+
|
|
437
|
+
`LlmOption` is now a floor rather than the whole shape: `setLlm` carries whatever else it is handed to the role, so
|
|
438
|
+
an adaptor of your own declares `interface MyLlmOption extends LlmOption { region: string }`, reads it with
|
|
439
|
+
`use<MyLlmOption>()`, and stops needing a config channel beside `setLlm`.
|
|
440
|
+
|
|
441
|
+
#### The CLI, in scripts and CI
|
|
442
|
+
|
|
443
|
+
`AiSession` and every command that drove it are removed. There is no replacement command — the workspace's agent
|
|
444
|
+
surface (`akan context`, the MCP tools, `akan guideline show`) is what feeds a coding agent now.
|
|
445
|
+
|
|
446
|
+
| Removed | What to do instead |
|
|
447
|
+
|---|---|
|
|
448
|
+
| `akan create-module <name> --ai`, `akan create-scalar <name> --ai` | Drop the flag. The command scaffolds the same files it always did; write the constant and dictionary yourself or let a coding agent do it against `get_guideline modelConstant` / `modelDictionary` |
|
|
449
|
+
| `akan compact <app-or-lib>` | Edit the `*.abstract.md` by hand. `akan quality scan` still warns past 300 lines |
|
|
450
|
+
| `akan set-llm`, `akan reset-llm`, `akan ask` | Nothing. The runtime LLM is `option.setLlm(...)` and has never been this config |
|
|
451
|
+
| `akan generate-instruction`, `akan update-instruction`, `akan generate-document`, `akan reapply-instruction` | Nothing. `akan guideline list` / `show` still read the bundled guidelines |
|
|
452
|
+
| `import { AiSession } from "@akanjs/devkit/aiEditor"` | Nothing — the module is gone |
|
|
453
|
+
|
|
454
|
+
`~/.akan/config.json` keeps an `llm` entry if it had one. It is ignored; delete it, because it holds an API key.
|
|
455
|
+
|
|
456
|
+
Then: `bun run akan typecheck <app>`, `bun run akan lint <app>`, and one agent turn in the browser to confirm the
|
|
457
|
+
provider answers.
|
|
458
|
+
|
|
396
459
|
### Data Flow Summary
|
|
397
460
|
|
|
398
461
|
For each business question, follow this chain:
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
import {
|
|
3
|
+
ApplicationBuildReporter,
|
|
4
|
+
HmrWatcher,
|
|
5
|
+
WatchRootResolver
|
|
6
|
+
} from "./index-khpfpcsd.js";
|
|
2
7
|
import {
|
|
3
8
|
CodegenLock,
|
|
4
9
|
WorkspaceExecutor,
|
|
5
10
|
AppExecutor
|
|
6
|
-
} from "./index-
|
|
7
|
-
import"./index-
|
|
8
|
-
import"./index-6wwzs1bc.js";
|
|
11
|
+
} from "./index-ga6hygxr.js";
|
|
12
|
+
import"./index-5b6tavv3.js";
|
|
9
13
|
import {
|
|
10
14
|
GraphClientEntryDiscovery,
|
|
11
15
|
RouteClientBuilder,
|
|
12
16
|
AutoImportSync,
|
|
13
17
|
DevChangePlanner,
|
|
14
18
|
DevGeneratedIndexSync
|
|
15
|
-
} from "./index-
|
|
16
|
-
import {
|
|
17
|
-
HmrWatcher,
|
|
18
|
-
WatchRootResolver
|
|
19
|
-
} from "./index-hwkygn9s.js";
|
|
19
|
+
} from "./index-fgq75haa.js";
|
|
20
20
|
import {
|
|
21
21
|
prepareDevWatchBatch
|
|
22
22
|
} from "./index-a5vmm6x4.js";
|
|
@@ -30,12 +30,6 @@ import path from "path";
|
|
|
30
30
|
import { Logger } from "akanjs/common";
|
|
31
31
|
|
|
32
32
|
class BuildBatchRunner {
|
|
33
|
-
static #entryCandidates = (workspaceRoot) => [
|
|
34
|
-
path.join(workspaceRoot, "pkgs/@akanjs/devkit/incrementalBuilder/buildBatch.proc.ts"),
|
|
35
|
-
path.join(workspaceRoot, "node_modules/@akanjs/devkit/incrementalBuilder/buildBatch.proc.ts"),
|
|
36
|
-
path.join(import.meta.dir, "buildBatch.proc.js"),
|
|
37
|
-
path.join(import.meta.dir, "buildBatch.proc.ts")
|
|
38
|
-
];
|
|
39
33
|
#logger = new Logger("BuildBatchRunner");
|
|
40
34
|
#entry = null;
|
|
41
35
|
#workspaceRoot;
|
|
@@ -47,7 +41,12 @@ class BuildBatchRunner {
|
|
|
47
41
|
async#resolveEntry() {
|
|
48
42
|
if (this.#entry)
|
|
49
43
|
return this.#entry;
|
|
50
|
-
const candidates =
|
|
44
|
+
const candidates = [
|
|
45
|
+
path.join(this.#workspaceRoot, "pkgs/@akanjs/devkit/incrementalBuilder/buildBatch.proc.ts"),
|
|
46
|
+
path.join(this.#workspaceRoot, "node_modules/@akanjs/devkit/incrementalBuilder/buildBatch.proc.ts"),
|
|
47
|
+
path.join(import.meta.dir, "buildBatch.proc.js"),
|
|
48
|
+
path.join(import.meta.dir, "buildBatch.proc.ts")
|
|
49
|
+
];
|
|
51
50
|
for (const candidate of candidates) {
|
|
52
51
|
if (!await Bun.file(candidate).exists())
|
|
53
52
|
continue;
|
|
@@ -173,6 +172,7 @@ class IncrementalBuilder {
|
|
|
173
172
|
app: this.#app,
|
|
174
173
|
routeId: msg.routeId,
|
|
175
174
|
seeds: msg.seeds,
|
|
175
|
+
graphSeeds: msg.graphSeeds,
|
|
176
176
|
artifact: this.#artifact,
|
|
177
177
|
knownEntries: new Set(msg.knownEntries),
|
|
178
178
|
discovery: this.#discovery
|
|
@@ -195,7 +195,7 @@ class IncrementalBuilder {
|
|
|
195
195
|
}
|
|
196
196
|
};
|
|
197
197
|
} catch (err) {
|
|
198
|
-
const errMsg = err
|
|
198
|
+
const errMsg = ApplicationBuildReporter.formatError(err, this.#app.workspace.workspaceRoot);
|
|
199
199
|
this.#logger.error(`build-route failed routeId=${msg.routeId}: ${errMsg}`);
|
|
200
200
|
this.#sendBuildStatus("route", { generation: msg.generation, ok: false, files: msg.seeds, message: errMsg });
|
|
201
201
|
return { type: "build-route-res", id: msg.id, ok: false, error: errMsg };
|
|
@@ -335,20 +335,19 @@ class IncrementalBuilder {
|
|
|
335
335
|
}, 150);
|
|
336
336
|
}
|
|
337
337
|
async installWatcher() {
|
|
338
|
-
const artifactDir = this.#artifactDir;
|
|
339
338
|
const roots = await new WatchRootResolver(this.#app).resolve();
|
|
340
339
|
const watcher = new HmrWatcher({
|
|
341
340
|
roots,
|
|
342
341
|
logger: this.#logger,
|
|
343
342
|
onBatch: async (batch) => {
|
|
344
|
-
await this.#enqueueWork("hmr-batch", async () => this.#handleWatchBatch(
|
|
343
|
+
await this.#enqueueWork("hmr-batch", async () => this.#handleWatchBatch(batch));
|
|
345
344
|
}
|
|
346
345
|
});
|
|
347
346
|
await watcher.start();
|
|
348
347
|
this.#watcher = watcher;
|
|
349
348
|
this.#logger.verbose(`watching ${roots.length} roots`);
|
|
350
349
|
}
|
|
351
|
-
async#handleWatchBatch(
|
|
350
|
+
async#handleWatchBatch(batch) {
|
|
352
351
|
const rawKinds = new Set(batch.kinds);
|
|
353
352
|
if (rawKinds.size === 0)
|
|
354
353
|
return;
|
|
@@ -401,7 +400,7 @@ class IncrementalBuilder {
|
|
|
401
400
|
}
|
|
402
401
|
const needs = [];
|
|
403
402
|
if (kinds.includes("code") && rebuildClient) {
|
|
404
|
-
if (this.#
|
|
403
|
+
if (this.#csrActive)
|
|
405
404
|
needs.push("csr");
|
|
406
405
|
else
|
|
407
406
|
this.#logger.verbose(`csr-rebundle skipped; request /__csr or ?csr=true (or set AKAN_DEV_CSR_REBUILD=1) to enable per-save CSR rebuilds`);
|
|
@@ -416,13 +415,10 @@ class IncrementalBuilder {
|
|
|
416
415
|
this.#logger.verbose(`css-rebuild scheduled generation=${generation}`);
|
|
417
416
|
}
|
|
418
417
|
}
|
|
419
|
-
async#runBatch({
|
|
420
|
-
generation,
|
|
421
|
-
needs,
|
|
422
|
-
changedFiles
|
|
423
|
-
}) {
|
|
418
|
+
async#runBatch(work) {
|
|
419
|
+
const { generation, needs, changedFiles } = work;
|
|
424
420
|
const started = Date.now();
|
|
425
|
-
const result = await this.#batchRunner.run(await this.#batchRequest(
|
|
421
|
+
const result = await this.#batchRunner.run(await this.#batchRequest(work), (msg) => BuilderChannel.emit(msg));
|
|
426
422
|
if (result.optimizedFonts)
|
|
427
423
|
this.#optimizedFonts = result.optimizedFonts;
|
|
428
424
|
if (result.cssAssets)
|
|
@@ -436,11 +432,7 @@ class IncrementalBuilder {
|
|
|
436
432
|
this.#logger.verbose(`css-rebuild checked (${Date.now() - started}ms)`);
|
|
437
433
|
return result;
|
|
438
434
|
}
|
|
439
|
-
async#batchRequest({
|
|
440
|
-
generation,
|
|
441
|
-
needs,
|
|
442
|
-
changedFiles
|
|
443
|
-
}) {
|
|
435
|
+
async#batchRequest({ generation, needs, changedFiles }) {
|
|
444
436
|
return {
|
|
445
437
|
appName: this.#app.name,
|
|
446
438
|
workspaceRoot: this.#app.workspace.workspaceRoot,
|
|
@@ -505,9 +497,6 @@ class IncrementalBuilder {
|
|
|
505
497
|
await BuilderChannel.send({ type: "build-csr-res", id: msg.id, ok: true });
|
|
506
498
|
});
|
|
507
499
|
}
|
|
508
|
-
#shouldRebuildCsr() {
|
|
509
|
-
return this.#csrActive;
|
|
510
|
-
}
|
|
511
500
|
static #csrArmedByEnv() {
|
|
512
501
|
return process.env.AKAN_DEV_CSR_REBUILD === "1";
|
|
513
502
|
}
|
|
@@ -606,23 +595,17 @@ class IncrementalBuilder {
|
|
|
606
595
|
builder.shutdown(msg.reason);
|
|
607
596
|
return;
|
|
608
597
|
}
|
|
609
|
-
if (msg.type
|
|
598
|
+
if (msg.type !== "build-route" && msg.type !== "build-csr")
|
|
599
|
+
return;
|
|
600
|
+
if (!builder || builder.shuttingDown) {
|
|
610
601
|
const error = builder?.shuttingDown ? recyclingError : bootingError;
|
|
611
|
-
|
|
612
|
-
BuilderChannel.emit({ type: "build-route-res", id: msg.id, ok: false, error });
|
|
613
|
-
return;
|
|
614
|
-
}
|
|
615
|
-
builder.handleBuildRoute(msg);
|
|
602
|
+
BuilderChannel.emit({ type: `${msg.type}-res`, id: msg.id, ok: false, error });
|
|
616
603
|
return;
|
|
617
604
|
}
|
|
618
|
-
if (msg.type === "build-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
BuilderChannel.emit({ type: "build-csr-res", id: msg.id, ok: false, error });
|
|
622
|
-
return;
|
|
623
|
-
}
|
|
605
|
+
if (msg.type === "build-route")
|
|
606
|
+
builder.handleBuildRoute(msg);
|
|
607
|
+
else
|
|
624
608
|
builder.handleBuildCsr(msg);
|
|
625
|
-
}
|
|
626
609
|
});
|
|
627
610
|
process.on("disconnect", () => {
|
|
628
611
|
logger.warn("host IPC channel closed; exiting builder");
|
|
@@ -647,7 +630,7 @@ class IncrementalBuilder {
|
|
|
647
630
|
await builder.rearmCsrFromEnv();
|
|
648
631
|
}
|
|
649
632
|
}
|
|
650
|
-
IncrementalBuilder.main().catch((err) => {
|
|
633
|
+
await IncrementalBuilder.main().catch((err) => {
|
|
651
634
|
console.error(err);
|
|
652
635
|
process.exit(1);
|
|
653
636
|
});
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
getDirname2
|
|
4
|
-
} from "./index-
|
|
4
|
+
} from "./index-5b6tavv3.js";
|
|
5
5
|
|
|
6
6
|
// pkgs/@akanjs/devkit/prompter.ts
|
|
7
7
|
import fsPromise from "fs/promises";
|
|
8
8
|
import { input, select } from "@inquirer/prompts";
|
|
9
|
-
class
|
|
9
|
+
class Prompter2 {
|
|
10
10
|
static async#getGuidelineRoot() {
|
|
11
11
|
const dirname = getDirname2(import.meta.url);
|
|
12
12
|
const candidates = [`${dirname}/guidelines`, `${dirname}/../cli/guidelines`];
|
|
@@ -19,27 +19,21 @@ class Prompter {
|
|
|
19
19
|
return candidates[0];
|
|
20
20
|
}
|
|
21
21
|
static async selectGuideline() {
|
|
22
|
-
const guideNames = await
|
|
22
|
+
const guideNames = await Prompter2.listGuidelines();
|
|
23
23
|
return await select({
|
|
24
24
|
message: "Select a guideline",
|
|
25
25
|
choices: guideNames.map((name) => ({ name, value: name }))
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
28
|
static async listGuidelines() {
|
|
29
|
-
const guidelineRoot = await
|
|
29
|
+
const guidelineRoot = await Prompter2.#getGuidelineRoot();
|
|
30
30
|
return (await fsPromise.readdir(guidelineRoot)).filter((name) => !name.startsWith("_")).sort();
|
|
31
31
|
}
|
|
32
|
-
static async getGuideJson(guideName) {
|
|
33
|
-
const guidelineRoot = await Prompter.#getGuidelineRoot();
|
|
34
|
-
const filePath = `${guidelineRoot}/${guideName}/${guideName}.generate.json`;
|
|
35
|
-
const guideJson = await fsPromise.readFile(filePath, "utf-8");
|
|
36
|
-
return JSON.parse(guideJson);
|
|
37
|
-
}
|
|
38
32
|
static async getInstruction(guideName) {
|
|
39
|
-
const guideNames = await
|
|
33
|
+
const guideNames = await Prompter2.listGuidelines();
|
|
40
34
|
if (!guideNames.includes(guideName))
|
|
41
35
|
throw new Error(`Unknown guideline "${guideName}". Available: ${guideNames.join(", ")}`);
|
|
42
|
-
const guidelineRoot = await
|
|
36
|
+
const guidelineRoot = await Prompter2.#getGuidelineRoot();
|
|
43
37
|
return await fsPromise.readFile(`${guidelineRoot}/${guideName}/${guideName}.instruction.md`, "utf-8");
|
|
44
38
|
}
|
|
45
39
|
static async getUpdateRequest(guideName) {
|
|
@@ -67,7 +61,7 @@ ${request}
|
|
|
67
61
|
`;
|
|
68
62
|
}
|
|
69
63
|
async getDocumentation(guideName) {
|
|
70
|
-
const guidelineRoot = await
|
|
64
|
+
const guidelineRoot = await Prompter2.#getGuidelineRoot();
|
|
71
65
|
const filePath = `${guidelineRoot}/${guideName}/${guideName}.instruction.md`;
|
|
72
66
|
const document = await fsPromise.readFile(filePath, "utf-8");
|
|
73
67
|
return `\`\`\`markdown
|
|
@@ -77,4 +71,4 @@ ${document}
|
|
|
77
71
|
}
|
|
78
72
|
}
|
|
79
73
|
|
|
80
|
-
export {
|
|
74
|
+
export { Prompter2 };
|
|
@@ -2,23 +2,21 @@
|
|
|
2
2
|
import {
|
|
3
3
|
runner,
|
|
4
4
|
script
|
|
5
|
-
} from "./index-
|
|
5
|
+
} from "./index-rmcw8yc8.js";
|
|
6
6
|
import {
|
|
7
7
|
LibExecutor
|
|
8
|
-
} from "./index-
|
|
8
|
+
} from "./index-ga6hygxr.js";
|
|
9
9
|
import {
|
|
10
10
|
LibSource,
|
|
11
11
|
formatLibStatuses
|
|
12
|
-
} from "./index-
|
|
12
|
+
} from "./index-bjwxs904.js";
|
|
13
13
|
|
|
14
14
|
// pkgs/@akanjs/cli/library/library.script.ts
|
|
15
15
|
import { Logger } from "akanjs/common";
|
|
16
16
|
|
|
17
17
|
// pkgs/@akanjs/devkit/semver.ts
|
|
18
18
|
var stripRangeOperator = (version) => version.replace(/^[\s^~>=<v]+/, "").trim();
|
|
19
|
-
var parseVersion = (version) =>
|
|
20
|
-
return version.replace(/^[^\d]*/, "").split(/[.-]/).map((part) => Number.parseInt(part, 10)).map((part) => Number.isFinite(part) ? part : 0);
|
|
21
|
-
};
|
|
19
|
+
var parseVersion = (version) => version.replace(/^[^\d]*/, "").split(/[.-]/).map((part) => Number.parseInt(part, 10)).map((part) => Number.isFinite(part) ? part : 0);
|
|
22
20
|
var compareNumeric = (a, b) => {
|
|
23
21
|
const left = parseVersion(a);
|
|
24
22
|
const right = parseVersion(b);
|
|
@@ -44,8 +42,7 @@ class LibraryRunner extends runner("library") {
|
|
|
44
42
|
async createLibrary(libName, workspace) {
|
|
45
43
|
await workspace.mkdir(`libs/${libName}`);
|
|
46
44
|
await workspace.applyTemplate({ basePath: `libs/${libName}`, template: "libRoot", dict: { libName } });
|
|
47
|
-
|
|
48
|
-
return lib;
|
|
45
|
+
return LibExecutor.from(workspace, libName);
|
|
49
46
|
}
|
|
50
47
|
async removeLibrary(lib) {
|
|
51
48
|
await lib.workspace.removeDir(`libs/${lib.name}`);
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
FileSys2
|
|
4
|
+
} from "./index-5b6tavv3.js";
|
|
5
|
+
import {
|
|
6
|
+
ConfigLock
|
|
7
|
+
} from "./index-3hx1z96c.js";
|
|
8
|
+
|
|
9
|
+
// pkgs/@akanjs/devkit/cloud/globalConfig.ts
|
|
10
|
+
import { mkdir, rename, rm, writeFile } from "fs/promises";
|
|
11
|
+
import dayjs from "dayjs";
|
|
12
|
+
|
|
13
|
+
// pkgs/@akanjs/devkit/cloud/constants.ts
|
|
14
|
+
import path from "path";
|
|
15
|
+
var basePath = path.join(Bun.env.HOME ?? Bun.env.USERPROFILE ?? "", ".akan");
|
|
16
|
+
var configPath = path.join(basePath, "config.json");
|
|
17
|
+
var getDefaultHostConfig = (host = GlobalConfig.akanCloudHost) => ({ host });
|
|
18
|
+
var defaultAkanGlobalConfig = {
|
|
19
|
+
cloudHost: {},
|
|
20
|
+
remoteEnvServers: {},
|
|
21
|
+
testTargets: {}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// pkgs/@akanjs/devkit/cloud/globalConfig.ts
|
|
25
|
+
class GlobalConfig {
|
|
26
|
+
static akanCloudHost = process.env.USE_AKANJS_PKGS === "true" ? `http://localhost:${process.env.CLOUD_HOST_PORT ?? 8283}` : "https://cloud.akanjs.com";
|
|
27
|
+
static async#getAkanGlobalConfig() {
|
|
28
|
+
const exists = await FileSys2.fileExists(configPath);
|
|
29
|
+
const akanConfig = exists ? await FileSys2.readJson(configPath) : {};
|
|
30
|
+
return {
|
|
31
|
+
...defaultAkanGlobalConfig,
|
|
32
|
+
...akanConfig,
|
|
33
|
+
cloudHost: akanConfig.cloudHost ?? defaultAkanGlobalConfig.cloudHost,
|
|
34
|
+
remoteEnvServers: akanConfig.remoteEnvServers ?? defaultAkanGlobalConfig.remoteEnvServers,
|
|
35
|
+
testTargets: akanConfig.testTargets ?? defaultAkanGlobalConfig.testTargets
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
static async#setAkanGlobalConfig(akanConfig) {
|
|
39
|
+
await mkdir(basePath, { recursive: true, mode: 448 });
|
|
40
|
+
const temp = `${configPath}.${process.pid}.${crypto.randomUUID()}.tmp`;
|
|
41
|
+
try {
|
|
42
|
+
await writeFile(temp, JSON.stringify(akanConfig, null, 2), { mode: 384 });
|
|
43
|
+
await rename(temp, configPath);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
await rm(temp, { force: true });
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
static #lockPath = `${configPath}.lock`;
|
|
50
|
+
static async#update(mutate) {
|
|
51
|
+
await ConfigLock.run(GlobalConfig.#lockPath, async () => {
|
|
52
|
+
await GlobalConfig.#setAkanGlobalConfig(mutate(await GlobalConfig.#getAkanGlobalConfig()));
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
static async getHostConfig(host = GlobalConfig.akanCloudHost) {
|
|
56
|
+
const akanConfig = await GlobalConfig.#getAkanGlobalConfig();
|
|
57
|
+
return GlobalConfig.toHostConfig(akanConfig.cloudHost[host] ?? getDefaultHostConfig(host));
|
|
58
|
+
}
|
|
59
|
+
static async setHostConfig(config = getDefaultHostConfig()) {
|
|
60
|
+
await GlobalConfig.#update((akanConfig) => GlobalConfig.#withHost(akanConfig, config));
|
|
61
|
+
}
|
|
62
|
+
static #withHost(akanConfig, config) {
|
|
63
|
+
return {
|
|
64
|
+
...akanConfig,
|
|
65
|
+
cloudHost: { ...akanConfig.cloudHost, [config.host]: GlobalConfig.toHostConfigDto(config) }
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
static async refreshHostAuth(host, refresh) {
|
|
69
|
+
try {
|
|
70
|
+
return await ConfigLock.run(GlobalConfig.#lockPath, async () => {
|
|
71
|
+
const akanConfig = await GlobalConfig.#getAkanGlobalConfig();
|
|
72
|
+
const current = GlobalConfig.toHostConfig(akanConfig.cloudHost[host] ?? getDefaultHostConfig(host));
|
|
73
|
+
const accessToken = current.auth?.accessToken;
|
|
74
|
+
const refreshToken = accessToken?.refreshToken;
|
|
75
|
+
if (!accessToken || !refreshToken || !GlobalConfig.needRefreshToken(accessToken))
|
|
76
|
+
return current;
|
|
77
|
+
const claimed = {
|
|
78
|
+
...current,
|
|
79
|
+
auth: { ...current.auth, accessToken: { ...accessToken, refreshToken: null } }
|
|
80
|
+
};
|
|
81
|
+
await GlobalConfig.#setAkanGlobalConfig(GlobalConfig.#withHost(akanConfig, claimed));
|
|
82
|
+
const next = await refresh(refreshToken).catch(() => null);
|
|
83
|
+
if (!next?.jwt)
|
|
84
|
+
return claimed;
|
|
85
|
+
const refreshed = { ...current, auth: { ...current.auth, accessToken: next } };
|
|
86
|
+
await GlobalConfig.#setAkanGlobalConfig(GlobalConfig.#withHost(await GlobalConfig.#getAkanGlobalConfig(), refreshed));
|
|
87
|
+
return refreshed;
|
|
88
|
+
});
|
|
89
|
+
} catch {
|
|
90
|
+
return await GlobalConfig.getHostConfig(host);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
static async getRemoteEnvServers() {
|
|
94
|
+
return (await GlobalConfig.#getAkanGlobalConfig()).remoteEnvServers;
|
|
95
|
+
}
|
|
96
|
+
static async setRemoteEnvServer(name, config) {
|
|
97
|
+
await GlobalConfig.#update((akanConfig) => ({
|
|
98
|
+
...akanConfig,
|
|
99
|
+
remoteEnvServers: { ...akanConfig.remoteEnvServers, [name]: config }
|
|
100
|
+
}));
|
|
101
|
+
}
|
|
102
|
+
static async removeRemoteEnvServer(name) {
|
|
103
|
+
await GlobalConfig.#update((akanConfig) => {
|
|
104
|
+
const { [name]: _, ...remoteEnvServers } = akanConfig.remoteEnvServers;
|
|
105
|
+
return { ...akanConfig, remoteEnvServers };
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
static async getTestTargets() {
|
|
109
|
+
return (await GlobalConfig.#getAkanGlobalConfig()).testTargets;
|
|
110
|
+
}
|
|
111
|
+
static async setTestTargets(testTargets) {
|
|
112
|
+
await GlobalConfig.#update((akanConfig) => ({
|
|
113
|
+
...akanConfig,
|
|
114
|
+
testTargets: { ...akanConfig.testTargets, ...testTargets }
|
|
115
|
+
}));
|
|
116
|
+
}
|
|
117
|
+
static needRefreshToken(accessToken) {
|
|
118
|
+
return !!accessToken?.expiresAt?.isBefore(dayjs().add(1, "hour"));
|
|
119
|
+
}
|
|
120
|
+
static toAccessToken(accessToken) {
|
|
121
|
+
return {
|
|
122
|
+
jwt: accessToken.jwt,
|
|
123
|
+
refreshToken: accessToken.refreshToken ?? null,
|
|
124
|
+
expiresAt: accessToken.expiresAt ? dayjs(accessToken.expiresAt) : null
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
static toAccessTokenDto(accessToken) {
|
|
128
|
+
return {
|
|
129
|
+
jwt: accessToken.jwt,
|
|
130
|
+
refreshToken: accessToken.refreshToken ?? null,
|
|
131
|
+
expiresAt: accessToken.expiresAt?.toString() ?? null
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
static toHostConfigDto(hostConfig) {
|
|
135
|
+
return {
|
|
136
|
+
host: hostConfig.host,
|
|
137
|
+
auth: {
|
|
138
|
+
accessToken: hostConfig.auth?.accessToken ? GlobalConfig.toAccessTokenDto(hostConfig.auth.accessToken) : undefined,
|
|
139
|
+
self: hostConfig.auth?.self
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
static toHostConfig(hostConfigDto) {
|
|
144
|
+
return {
|
|
145
|
+
host: hostConfigDto.host,
|
|
146
|
+
auth: {
|
|
147
|
+
accessToken: hostConfigDto.auth?.accessToken ? GlobalConfig.toAccessToken(hostConfigDto.auth.accessToken) : undefined,
|
|
148
|
+
self: hostConfigDto.auth?.self
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// pkgs/@akanjs/devkit/cloud/cloudApi.ts
|
|
155
|
+
class HttpClient {
|
|
156
|
+
baseUrl;
|
|
157
|
+
headers = {};
|
|
158
|
+
constructor(baseUrl, headers = {}) {
|
|
159
|
+
this.baseUrl = baseUrl;
|
|
160
|
+
this.headers = headers;
|
|
161
|
+
}
|
|
162
|
+
async get(url, { headers, signal } = {}) {
|
|
163
|
+
const response = await fetch(`${this.baseUrl}${url}`, {
|
|
164
|
+
headers: { "Content-Type": "application/json", ...this.headers, ...headers },
|
|
165
|
+
...signal ? { signal } : {}
|
|
166
|
+
});
|
|
167
|
+
return await HttpClient.#body(response, url);
|
|
168
|
+
}
|
|
169
|
+
async getFile(url, localPath, headers) {
|
|
170
|
+
const response = await fetch(`${this.baseUrl}${url}`, {
|
|
171
|
+
headers: { ...this.headers, ...headers }
|
|
172
|
+
});
|
|
173
|
+
if (!response.ok)
|
|
174
|
+
throw new Error(`Failed to download file: ${response.status} ${response.statusText}`);
|
|
175
|
+
await Bun.write(localPath, response);
|
|
176
|
+
}
|
|
177
|
+
async post(url, data, { headers, signal } = {}) {
|
|
178
|
+
const isFormData = data instanceof FormData;
|
|
179
|
+
const response = await fetch(`${this.baseUrl}${url}`, {
|
|
180
|
+
method: "POST",
|
|
181
|
+
body: isFormData ? data : JSON.stringify(data),
|
|
182
|
+
headers: isFormData ? { ...this.headers, ...headers } : { "Content-Type": "application/json", ...this.headers, ...headers },
|
|
183
|
+
...signal ? { signal } : {}
|
|
184
|
+
});
|
|
185
|
+
return await HttpClient.#body(response, url);
|
|
186
|
+
}
|
|
187
|
+
static async#body(response, url) {
|
|
188
|
+
const text = await response.text();
|
|
189
|
+
if (!response.ok)
|
|
190
|
+
throw new Error(`${response.status} ${response.statusText} from ${url}: ${text.slice(0, 200)}`);
|
|
191
|
+
return JSON.parse(text);
|
|
192
|
+
}
|
|
193
|
+
setHeaders(headers) {
|
|
194
|
+
Object.assign(this.headers, headers);
|
|
195
|
+
return this;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
class CloudApi {
|
|
200
|
+
#api;
|
|
201
|
+
#accessToken = null;
|
|
202
|
+
#workspace;
|
|
203
|
+
host;
|
|
204
|
+
url;
|
|
205
|
+
static async fromHost(workspace, host) {
|
|
206
|
+
const hostConfig = await GlobalConfig.getHostConfig(host);
|
|
207
|
+
const accessToken = hostConfig.auth?.accessToken;
|
|
208
|
+
if (!accessToken || !GlobalConfig.needRefreshToken(accessToken))
|
|
209
|
+
return new CloudApi(workspace, hostConfig);
|
|
210
|
+
const tokenless = new CloudApi(workspace, hostConfig);
|
|
211
|
+
const refreshed = await GlobalConfig.refreshHostAuth(hostConfig.host, (refreshToken) => tokenless.refreshAuthToken(refreshToken));
|
|
212
|
+
return new CloudApi(workspace, refreshed);
|
|
213
|
+
}
|
|
214
|
+
constructor(workspace, hostConfig) {
|
|
215
|
+
this.#workspace = workspace;
|
|
216
|
+
this.#accessToken = hostConfig.auth?.accessToken ?? null;
|
|
217
|
+
this.host = hostConfig.host;
|
|
218
|
+
this.url = `${this.host}/api`;
|
|
219
|
+
this.#api = new HttpClient(this.url);
|
|
220
|
+
if (this.#accessToken && !GlobalConfig.needRefreshToken(this.#accessToken))
|
|
221
|
+
this.#api.setHeaders({ Authorization: `Bearer ${this.#accessToken.jwt}` });
|
|
222
|
+
}
|
|
223
|
+
#authorize(accessTokenDto) {
|
|
224
|
+
this.#accessToken = GlobalConfig.toAccessToken(accessTokenDto);
|
|
225
|
+
this.#api.setHeaders({ Authorization: `Bearer ${this.#accessToken.jwt}` });
|
|
226
|
+
return this.#accessToken;
|
|
227
|
+
}
|
|
228
|
+
async uploadEnv(devProjectId, file) {
|
|
229
|
+
const formData = new FormData;
|
|
230
|
+
formData.append("devProjectId", devProjectId);
|
|
231
|
+
formData.append("file", file);
|
|
232
|
+
return await this.#api.post(`/uploadEnv/${devProjectId}`, formData);
|
|
233
|
+
}
|
|
234
|
+
async downloadEnv(devProjectId) {
|
|
235
|
+
const localPath = `${this.#workspace.workspaceRoot}/local/env.tar`;
|
|
236
|
+
await this.#api.getFile(`/downloadEnv/${devProjectId}`, localPath);
|
|
237
|
+
return localPath;
|
|
238
|
+
}
|
|
239
|
+
async getRemoteAuthToken(remoteId) {
|
|
240
|
+
try {
|
|
241
|
+
return this.#authorize(await this.#api.get(`/getRemoteAuthToken/${remoteId}`));
|
|
242
|
+
} catch (_) {
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
async refreshAuthToken(refreshToken) {
|
|
247
|
+
return this.#authorize(await this.#api.post(`/refreshAuthToken`, { refreshToken }, { signal: AbortSignal.timeout(20000) }));
|
|
248
|
+
}
|
|
249
|
+
async requestTunnel(input) {
|
|
250
|
+
return await this.#api.post(`/tunnel/requestTunnel`, input);
|
|
251
|
+
}
|
|
252
|
+
async tunnelListInSelf() {
|
|
253
|
+
return await this.#api.get(`/tunnel/tunnelListInSelf`);
|
|
254
|
+
}
|
|
255
|
+
async revokeTunnel(code) {
|
|
256
|
+
return await this.#api.post(`/tunnel/revokeTunnel`, { code }, { signal: AbortSignal.timeout(5000) });
|
|
257
|
+
}
|
|
258
|
+
async getRemoteSelf() {
|
|
259
|
+
try {
|
|
260
|
+
return await this.#api.get(`/getRemoteSelf`);
|
|
261
|
+
} catch {
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
export { getDefaultHostConfig, GlobalConfig, CloudApi };
|