@astrale-os/adapter-cloudflare 0.1.9 → 0.2.0
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/package.json +1 -1
- package/template/.agents/skills/astrale-cli/SKILL.md +1 -1
- package/template/.agents/skills/astrale-domain/SKILL.md +5 -5
- package/template/.env.example +5 -7
- package/template/README.md +2 -2
- package/template/client/README.md +81 -62
- package/template/client/__tests__/app.test.tsx +143 -98
- package/template/client/__tests__/harness.ts +62 -12
- package/template/client/__tests__/kernel.test.ts +40 -51
- package/template/client/__tests__/seam.test.tsx +115 -0
- package/template/client/index.html +1 -1
- package/template/client/package.json +1 -0
- package/template/client/src/app.tsx +34 -83
- package/template/client/src/main.tsx +2 -2
- package/template/client/src/shell/client.ts +67 -0
- package/template/client/src/shell/index.ts +20 -0
- package/template/client/src/shell/invoke.ts +35 -0
- package/template/client/src/shell/transformers.ts +72 -0
- package/template/client/src/shell/use-async.ts +56 -0
- package/template/client/src/shell/use-capability.ts +59 -0
- package/template/client/src/shell/use-node.ts +61 -0
- package/template/client/src/shell/use-shell.ts +91 -0
- package/template/client/src/shell/view-router.tsx +97 -0
- package/template/client/src/status/components/StatusCard.tsx +50 -0
- package/template/client/src/status/components/index.ts +1 -0
- package/template/client/src/status/hooks/index.ts +3 -0
- package/template/client/src/status/hooks/useCheck.mutation.ts +16 -0
- package/template/client/src/status/hooks/useCheckable.query.ts +64 -0
- package/template/client/src/status/index.ts +7 -0
- package/template/client/src/status/status.api.ts +12 -0
- package/template/client/src/status/status.mappers.ts +19 -0
- package/template/client/src/status/status.types.ts +11 -0
- package/template/client/src/styles.css +182 -4
- package/template/client/src/ui/StatusBadge.tsx +31 -0
- package/template/client/src/ui/format.ts +24 -0
- package/template/client/src/ui/index.ts +13 -0
- package/template/client/src/ui/surface.tsx +56 -0
- package/template/client/src/ui/value.tsx +32 -0
- package/template/client/src/views/status.tsx +28 -0
- package/template/client/tsconfig.json +2 -1
- package/template/client/vite.config.ts +11 -13
- package/template/client/vitest.config.ts +11 -5
- package/template/core/monitor/health.ts +34 -0
- package/template/core/monitor/index.ts +9 -0
- package/template/core/monitor/keys.ts +41 -0
- package/template/core/monitor/node.ts +57 -0
- package/template/deps.ts +10 -9
- package/template/domain.ts +1 -1
- package/template/env.ts +2 -9
- package/template/integrations/prober/http.ts +32 -0
- package/template/integrations/prober/mock.ts +18 -0
- package/template/integrations/prober/port.ts +26 -0
- package/template/integrations/prober/registry.ts +65 -0
- package/template/package.json +1 -1
- package/template/pnpm-lock.yaml +2766 -0
- package/template/runtime/index.ts +63 -34
- package/template/runtime/monitor/check.ts +29 -0
- package/template/runtime/monitor/index.ts +9 -0
- package/template/runtime/monitor/seed.ts +95 -0
- package/template/runtime/monitor/watch.ts +31 -0
- package/template/runtime/shared.ts +21 -0
- package/template/runtime/status-page/add.ts +21 -0
- package/template/runtime/status-page/check.ts +50 -0
- package/template/runtime/status-page/create.ts +24 -0
- package/template/runtime/status-page/index.ts +8 -0
- package/template/schema/index.ts +11 -4
- package/template/schema/monitor.ts +94 -0
- package/template/views/index.ts +8 -2
- package/template/views/status-page.ts +16 -0
- package/template/client/src/lib/kernel.ts +0 -135
- package/template/client/src/lib/shell.ts +0 -197
- package/template/client/src/lib/use-node.ts +0 -66
- package/template/client/src/lib/use-shell.ts +0 -85
- package/template/core/keys.ts +0 -28
- package/template/core/note.ts +0 -148
- package/template/integrations/summary/heuristic.ts +0 -25
- package/template/integrations/summary/http.ts +0 -69
- package/template/integrations/summary/port.ts +0 -21
- package/template/integrations/summary/registry.ts +0 -52
- package/template/schema/note.ts +0 -67
- package/template/views/note.ts +0 -21
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HTTP summarizer — the REAL external-API adapter: a single OpenAI-compatible
|
|
3
|
-
* `POST /chat/completions` call. This is the shape your domain's external calls
|
|
4
|
-
* take — config + secret from `env`, a timeout via `AbortSignal`, upstream
|
|
5
|
-
* detail preserved in `cause`. Selected by `NOTE_SUMMARIZER=http` (registry.ts);
|
|
6
|
-
* the default stays the no-network `heuristic` adapter so a fresh scaffold needs
|
|
7
|
-
* no secret.
|
|
8
|
-
*
|
|
9
|
-
* `baseUrl` is anything OpenAI-compatible — `https://api.openai.com/v1`, a
|
|
10
|
-
* Workers-AI gateway, or your OWN `ai-gateway` domain's `/v1` surface. On a soft
|
|
11
|
-
* upstream failure it falls back to the heuristic rather than throwing: a flaky
|
|
12
|
-
* summarizer must never block creating a note (see the port contract).
|
|
13
|
-
*/
|
|
14
|
-
import { createHeuristicSummarizer } from './heuristic'
|
|
15
|
-
import type { Summarizer } from './port'
|
|
16
|
-
|
|
17
|
-
export interface HttpSummarizerConfig {
|
|
18
|
-
/** Bearer token for the upstream (a secret — ships via `.env.<env>`). */
|
|
19
|
-
apiKey: string
|
|
20
|
-
/** OpenAI-compatible base URL, e.g. `https://api.openai.com/v1`. */
|
|
21
|
-
baseUrl: string
|
|
22
|
-
/** Model id, e.g. `gpt-4o-mini`. */
|
|
23
|
-
model: string
|
|
24
|
-
/** Upstream request timeout in ms (default 15000). */
|
|
25
|
-
timeoutMs?: number
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const DEFAULT_TIMEOUT_MS = 15_000
|
|
29
|
-
const SYSTEM_PROMPT = 'Summarize the user note in one short sentence. Reply with the summary only.'
|
|
30
|
-
|
|
31
|
-
/** Build the OpenAI-compatible HTTP summarizer (with a heuristic fallback). */
|
|
32
|
-
export function createHttpSummarizer(config: HttpSummarizerConfig): Summarizer {
|
|
33
|
-
const fallback = createHeuristicSummarizer()
|
|
34
|
-
const endpoint = `${config.baseUrl.replace(/\/+$/, '')}/chat/completions`
|
|
35
|
-
return {
|
|
36
|
-
async summarize(body) {
|
|
37
|
-
try {
|
|
38
|
-
const res = await fetch(endpoint, {
|
|
39
|
-
method: 'POST',
|
|
40
|
-
headers: {
|
|
41
|
-
'content-type': 'application/json',
|
|
42
|
-
authorization: `Bearer ${config.apiKey}`,
|
|
43
|
-
},
|
|
44
|
-
body: JSON.stringify({
|
|
45
|
-
model: config.model,
|
|
46
|
-
messages: [
|
|
47
|
-
{ role: 'system', content: SYSTEM_PROMPT },
|
|
48
|
-
{ role: 'user', content: body },
|
|
49
|
-
],
|
|
50
|
-
}),
|
|
51
|
-
signal: AbortSignal.timeout(config.timeoutMs ?? DEFAULT_TIMEOUT_MS),
|
|
52
|
-
})
|
|
53
|
-
if (!res.ok) {
|
|
54
|
-
throw new Error(`summarizer upstream returned ${res.status}`, {
|
|
55
|
-
cause: await res.text().catch(() => undefined),
|
|
56
|
-
})
|
|
57
|
-
}
|
|
58
|
-
const data = (await res.json()) as {
|
|
59
|
-
choices?: Array<{ message?: { content?: string } }>
|
|
60
|
-
}
|
|
61
|
-
const text = data.choices?.[0]?.message?.content?.trim()
|
|
62
|
-
return text || (await fallback.summarize(body))
|
|
63
|
-
} catch {
|
|
64
|
-
// Soft-fail: never block a note create on the summarizer.
|
|
65
|
-
return fallback.summarize(body)
|
|
66
|
-
}
|
|
67
|
-
},
|
|
68
|
-
}
|
|
69
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Summarizer — the PORT between the domain's logic and whatever produces a
|
|
3
|
-
* note's one-line summary. This is the external-API seam every real domain has
|
|
4
|
-
* (here: an LLM/summarization API), reduced to the narrowest interface the
|
|
5
|
-
* logic needs.
|
|
6
|
-
*
|
|
7
|
-
* `core/` logic depends on THIS interface only — never on `fetch`, an SDK, or
|
|
8
|
-
* `env`. Adapters in this folder implement it: `heuristic.ts` (the zero-config
|
|
9
|
-
* default, no network) and `http.ts` (a real OpenAI-compatible call). The
|
|
10
|
-
* registry picks one from `env`; the handler logic only ever sees the resolved
|
|
11
|
-
* port. Swap or add a provider = one adapter + one arm in `registry.ts`.
|
|
12
|
-
*/
|
|
13
|
-
export interface Summarizer {
|
|
14
|
-
/**
|
|
15
|
-
* Produce a short, single-line summary of `body`. Called once per note
|
|
16
|
-
* create/seed, so keep it fast and resilient. Adapters MUST resolve to a
|
|
17
|
-
* usable string (fall back to a heuristic rather than throwing on a soft
|
|
18
|
-
* upstream failure) — a flaky summarizer should never block creating a note.
|
|
19
|
-
*/
|
|
20
|
-
summarize(body: string): Promise<string>
|
|
21
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Summarizer registry — the ONE place the worker env becomes the live
|
|
3
|
-
* `Summarizer` port. Adding a provider = one more arm in `selectSummarizer`;
|
|
4
|
-
* handler logic only ever sees the resolved port.
|
|
5
|
-
*
|
|
6
|
-
* Selection is ON-REQUEST: the provider is chosen + built lazily on the first
|
|
7
|
-
* handler that calls `summarizer()`, then cached for the isolate — NOT at
|
|
8
|
-
* `deps()` construction. A worker left on the default never validates the HTTP
|
|
9
|
-
* provider's env, and a per-request override could slot in here later.
|
|
10
|
-
*/
|
|
11
|
-
import type { Env } from '../../env'
|
|
12
|
-
import { createHeuristicSummarizer } from './heuristic'
|
|
13
|
-
import { createHttpSummarizer } from './http'
|
|
14
|
-
import type { Summarizer } from './port'
|
|
15
|
-
|
|
16
|
-
export interface SummarizerRegistry {
|
|
17
|
-
/** Resolve the configured summarizer port (lazy + cached per isolate). */
|
|
18
|
-
summarizer(): Summarizer
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/** Build the registry from the worker env (the port is built lazily + cached). */
|
|
22
|
-
export function buildSummarizerRegistry(env: Env): SummarizerRegistry {
|
|
23
|
-
let cached: Summarizer | undefined
|
|
24
|
-
return {
|
|
25
|
-
summarizer() {
|
|
26
|
-
return (cached ??= selectSummarizer(env))
|
|
27
|
-
},
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const DEFAULT_HTTP_MODEL = 'gpt-4o-mini'
|
|
32
|
-
|
|
33
|
-
/** Bind the abstract summarizer port to the configured concrete adapter. */
|
|
34
|
-
function selectSummarizer(env: Env): Summarizer {
|
|
35
|
-
const provider = (env.NOTE_SUMMARIZER || 'heuristic').toLowerCase()
|
|
36
|
-
|
|
37
|
-
if (provider === 'http') {
|
|
38
|
-
if (!env.SUMMARIZER_API_KEY || !env.SUMMARIZER_BASE_URL) {
|
|
39
|
-
throw new Error(
|
|
40
|
-
'NOTE_SUMMARIZER=http requires SUMMARIZER_API_KEY and SUMMARIZER_BASE_URL',
|
|
41
|
-
)
|
|
42
|
-
}
|
|
43
|
-
return createHttpSummarizer({
|
|
44
|
-
apiKey: env.SUMMARIZER_API_KEY,
|
|
45
|
-
baseUrl: env.SUMMARIZER_BASE_URL,
|
|
46
|
-
model: env.SUMMARIZER_MODEL || DEFAULT_HTTP_MODEL,
|
|
47
|
-
})
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Default: the zero-config local heuristic (no network, no secret).
|
|
51
|
-
return createHeuristicSummarizer()
|
|
52
|
-
}
|
package/template/schema/note.ts
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Note context — the domain's single bounded slice.
|
|
3
|
-
*
|
|
4
|
-
* One file per context: a class (or a few tightly-related classes) plus the
|
|
5
|
-
* edges that bind them. Here that is the `NoteOps` interface, the `Note`
|
|
6
|
-
* class that implements it, and the `references` edge from one Note to
|
|
7
|
-
* another. To grow the domain, add `schema/<context>.ts` and register its
|
|
8
|
-
* members in `schema/index.ts`.
|
|
9
|
-
*
|
|
10
|
-
* - Interface `NoteOps` one static op, `createNote`. Static → the impl
|
|
11
|
-
* gets no `self`; it creates a brand-new Note.
|
|
12
|
-
* - Class `Note` implements `[NoteOps, Container]`, inheriting
|
|
13
|
-
* `createNote` and adding the instance method
|
|
14
|
-
* `reference` (links this Note to another).
|
|
15
|
-
* - Edge `references` Note → Note. Materialized at runtime by
|
|
16
|
-
* `reference` (and by `seed`).
|
|
17
|
-
*/
|
|
18
|
-
import { edgeClass, KernelSchema, nodeClass, nodeInterface } from '@astrale-os/kernel-core'
|
|
19
|
-
import { fn } from '@astrale-os/kernel-dsl'
|
|
20
|
-
import { z } from 'zod'
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Thin ref to a created node — what node-creating ops return. A remote method
|
|
24
|
-
* returns a plain `{ id, path }`, never `ref(SELF)` (whose full-Node value
|
|
25
|
-
* does not round-trip over the worker wire).
|
|
26
|
-
*/
|
|
27
|
-
export const NoteRef = z.object({ id: z.string(), path: z.string() })
|
|
28
|
-
|
|
29
|
-
export const NoteOps = nodeInterface({
|
|
30
|
-
methods: {
|
|
31
|
-
createNote: fn({
|
|
32
|
-
static: true,
|
|
33
|
-
params: { title: z.string(), body: z.string() },
|
|
34
|
-
returns: NoteRef,
|
|
35
|
-
}),
|
|
36
|
-
},
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
export const Note = nodeClass({
|
|
40
|
-
implements: [NoteOps, KernelSchema.interfaces.Container],
|
|
41
|
-
props: {
|
|
42
|
-
title: z.string(),
|
|
43
|
-
body: z.string(),
|
|
44
|
-
// One-line summary, stamped at create/seed time from the `Summarizer` port
|
|
45
|
-
// (see integrations/summary/) — the external-API seam wired through `deps`.
|
|
46
|
-
summary: z.string().optional(),
|
|
47
|
-
},
|
|
48
|
-
methods: {
|
|
49
|
-
reference: fn({
|
|
50
|
-
params: { target: z.string() },
|
|
51
|
-
returns: z.object({ linked: z.string() }),
|
|
52
|
-
}),
|
|
53
|
-
// Post-install bootstrap (wired as `postInstall` in astrale.config.ts).
|
|
54
|
-
// Static: the kernel calls it ONCE after install, as __SYSTEM__, with no
|
|
55
|
-
// `self`. Must stay idempotent — a re-install runs it again.
|
|
56
|
-
seed: fn({
|
|
57
|
-
static: true,
|
|
58
|
-
returns: z.object({ seeded: z.number().int() }),
|
|
59
|
-
}),
|
|
60
|
-
},
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
export const references = edgeClass(
|
|
64
|
-
{ as: 'from_note', types: [Note], cardinality: '0..*' },
|
|
65
|
-
{ as: 'to_note', types: [Note], cardinality: '0..*' },
|
|
66
|
-
{ props: { reason: z.string().optional() } },
|
|
67
|
-
)
|
package/template/views/note.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `ui-note` — a rich View backed by the `client/` React + Vite SPA (instead of
|
|
3
|
-
* an inline-HTML `render` like `welcome`). Because it declares `mount` rather
|
|
4
|
-
* than `render`, the View node's iframe binding points at `<serving url>/ui/note`
|
|
5
|
-
* — the SDK stamps it from the worker's live URL when it builds the install
|
|
6
|
-
* bundle. The Cloudflare adapter serves `/ui/*` from `../.dist` (built by
|
|
7
|
-
* `client/` with base `/ui/`) via the Worker's `ASSETS` binding.
|
|
8
|
-
*
|
|
9
|
-
* `viewFor: selfOf(Note)` attaches a `view_for` edge to the `Note` class
|
|
10
|
-
* meta-node, so the GUI offers this view for any Note instance.
|
|
11
|
-
*/
|
|
12
|
-
import { selfOf } from '@astrale-os/kernel-dsl'
|
|
13
|
-
import { defineView } from '@astrale-os/sdk'
|
|
14
|
-
|
|
15
|
-
import { Note } from '../schema/note'
|
|
16
|
-
|
|
17
|
-
export const note = defineView({
|
|
18
|
-
auth: 'public',
|
|
19
|
-
mount: '/ui/note',
|
|
20
|
-
viewFor: selfOf(Note),
|
|
21
|
-
})
|