@astrale-os/adapter-cloudflare 0.1.8 → 0.1.10
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/dist/assets-pack.d.ts +1 -1
- package/dist/assets-pack.js +1 -1
- package/dist/build.d.ts +15 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/build.js +15 -0
- package/dist/build.js.map +1 -0
- package/dist/client.d.ts +9 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +10 -1
- package/dist/client.js.map +1 -1
- package/dist/cloudflare.d.ts +15 -3
- package/dist/cloudflare.d.ts.map +1 -1
- package/dist/cloudflare.js +52 -18
- package/dist/cloudflare.js.map +1 -1
- package/dist/codegen/worker.d.ts +26 -6
- package/dist/codegen/worker.d.ts.map +1 -1
- package/dist/codegen/worker.js +67 -54
- package/dist/codegen/worker.js.map +1 -1
- package/dist/codegen/wrangler.d.ts +11 -2
- package/dist/codegen/wrangler.d.ts.map +1 -1
- package/dist/codegen/wrangler.js +11 -5
- package/dist/codegen/wrangler.js.map +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/params.d.ts +30 -30
- package/dist/params.d.ts.map +1 -1
- package/dist/parse-output.d.ts +1 -1
- package/dist/parse-output.js +1 -1
- package/package.json +6 -2
- package/src/assets-pack.ts +1 -1
- package/src/build.ts +15 -0
- package/src/client.ts +11 -1
- package/src/cloudflare.ts +53 -18
- package/src/codegen/worker.ts +76 -59
- package/src/codegen/wrangler.ts +15 -5
- package/src/index.ts +6 -3
- package/src/params.ts +32 -31
- package/src/parse-output.ts +1 -1
- package/template/.agents/skills/astrale-cli/SKILL.md +26 -12
- package/template/.agents/skills/astrale-domain/SKILL.md +46 -29
- package/template/.env.example +6 -0
- package/template/README.md +25 -10
- package/template/astrale.config.ts +27 -33
- package/template/client/README.md +80 -63
- package/template/client/__tests__/app.test.tsx +188 -99
- package/template/client/__tests__/harness.ts +67 -12
- package/template/client/__tests__/kernel.test.ts +65 -50
- package/template/client/__tests__/seam.test.tsx +111 -0
- package/template/client/index.html +1 -1
- package/template/client/package.json +1 -0
- package/template/client/src/app.tsx +40 -83
- package/template/client/src/main.tsx +2 -2
- package/template/client/src/monitor/components/MonitorCard.tsx +50 -0
- package/template/client/src/monitor/components/index.ts +1 -0
- package/template/client/src/monitor/hooks/index.ts +3 -0
- package/template/client/src/monitor/hooks/useCheck.mutation.ts +16 -0
- package/template/client/src/monitor/hooks/useMonitor.query.ts +64 -0
- package/template/client/src/monitor/index.ts +6 -0
- package/template/client/src/monitor/monitor.api.ts +11 -0
- package/template/client/src/monitor/monitor.mappers.ts +38 -0
- package/template/client/src/monitor/monitor.types.ts +23 -0
- package/template/client/src/monitor/ui/MonitorDetails.UI.tsx +38 -0
- package/template/client/src/monitor/ui/StatusBadge.UI.tsx +14 -0
- package/template/client/src/monitor/ui/index.ts +8 -0
- 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 +61 -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 +98 -0
- package/template/client/src/styles.css +177 -4
- package/template/client/src/ui/format.ts +24 -0
- package/template/client/src/ui/index.ts +9 -0
- package/template/client/src/ui/surface.tsx +56 -0
- package/template/client/src/ui/value.tsx +32 -0
- package/template/client/src/views/monitor.tsx +30 -0
- package/template/client/tsconfig.json +3 -2
- package/template/client/vite.config.ts +14 -15
- package/template/client/vitest.config.ts +12 -5
- package/template/core/monitor/health.ts +19 -0
- package/template/core/monitor/index.ts +9 -0
- package/template/core/monitor/keys.ts +29 -0
- package/template/core/monitor/node.ts +51 -0
- package/template/deps.ts +25 -0
- package/template/domain.ts +33 -0
- package/template/env.ts +4 -0
- package/template/integrations/prober/http.ts +43 -0
- package/template/integrations/prober/mock.ts +22 -0
- package/template/integrations/prober/port.ts +28 -0
- package/template/integrations/prober/registry.ts +66 -0
- package/template/package.json +2 -3
- package/template/pnpm-lock.yaml +2766 -0
- package/template/runtime/index.ts +79 -0
- package/template/runtime/monitor/check.ts +29 -0
- package/template/runtime/monitor/dependsOn.ts +16 -0
- package/template/runtime/monitor/index.ts +12 -0
- package/template/runtime/monitor/seed.ts +74 -0
- package/template/runtime/monitor/shared.ts +17 -0
- package/template/runtime/monitor/watch.ts +37 -0
- package/template/schema/index.ts +13 -4
- package/template/schema/monitor.ts +80 -0
- package/template/tsconfig.json +13 -2
- package/template/views/index.ts +9 -2
- package/template/views/monitor.ts +22 -0
- package/dist/astrale.d.ts +0 -27
- package/dist/astrale.d.ts.map +0 -1
- package/dist/astrale.js +0 -222
- package/dist/astrale.js.map +0 -1
- package/src/astrale.ts +0 -259
- 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/methods/index.ts +0 -66
- package/template/methods/note.ts +0 -131
- package/template/schema/compiled.ts +0 -14
- package/template/schema/note.ts +0 -64
- package/template/views/note.ts +0 -21
package/dist/astrale.js
DELETED
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `astrale(envs)` — the Astrale-managed deployment adapter.
|
|
3
|
-
*
|
|
4
|
-
* Ships the domain THROUGH the platform instead of to the author's own cloud
|
|
5
|
-
* account: `deploy` builds the same single-module workerd bundle the Cloudflare
|
|
6
|
-
* adapter ships (shared codegen + `wrangler deploy --dry-run`), then publishes
|
|
7
|
-
* it via the admin domain's catalog — `DomainPublished.register` →
|
|
8
|
-
* `::release { bundleBase64 }` (the worker stages the bytes in the platform
|
|
9
|
-
* registry) → `::install { instanceId, source: { kind: 'package' } }`, which
|
|
10
|
-
* deploys it as a host-local Service next to the target instance and installs
|
|
11
|
-
* the domain on it. No Cloudflare account, no wrangler auth — the only
|
|
12
|
-
* credential is the author's Astrale identity, supplied by the `astrale` CLI
|
|
13
|
-
* session this adapter shells out to (the same trust source as
|
|
14
|
-
* `astrale instance install`).
|
|
15
|
-
*
|
|
16
|
-
* `watch` is identical to the Cloudflare adapter's local dev (wrangler dev on
|
|
17
|
-
* localhost) — managed deploys change WHERE the worker runs, not how you
|
|
18
|
-
* iterate locally.
|
|
19
|
-
*
|
|
20
|
-
* Managed deploys ship the client SPA (served under `/ui` by the box) and
|
|
21
|
-
* runtime secrets (dotenv map on the install call, encrypted at rest
|
|
22
|
-
* platform-side; platform-managed env keys always win precedence).
|
|
23
|
-
*/
|
|
24
|
-
import { defineAdapter, loadDotenvFile } from '@astrale-os/devkit';
|
|
25
|
-
import { spawn } from 'node:child_process';
|
|
26
|
-
import { existsSync } from 'node:fs';
|
|
27
|
-
import { readFile } from 'node:fs/promises';
|
|
28
|
-
import { join } from 'node:path';
|
|
29
|
-
import { packAssets } from './assets-pack';
|
|
30
|
-
import { buildClient } from './client';
|
|
31
|
-
import { logTo, prepare } from './cloudflare';
|
|
32
|
-
import { runWranglerBundle, runWranglerDev } from './wrangler-cli';
|
|
33
|
-
const DEFAULT_PORT = 8787;
|
|
34
|
-
const DEFAULT_ADMIN_URL = 'https://admin.eu.astrale.ai/api';
|
|
35
|
-
const DEFAULT_REGION = 'eu';
|
|
36
|
-
const ADMIN_ORIGIN = 'admin.astrale.ai';
|
|
37
|
-
export function astrale(envs) {
|
|
38
|
-
return defineAdapter({
|
|
39
|
-
name: 'astrale',
|
|
40
|
-
envs,
|
|
41
|
-
// Local dev is unchanged: wrangler dev on localhost. Managed deploys change
|
|
42
|
-
// WHERE the worker ships, not the inner loop.
|
|
43
|
-
async watch(params, ctx) {
|
|
44
|
-
const { configPath } = await prepare({
|
|
45
|
-
...(params.vars ? { vars: params.vars } : {}),
|
|
46
|
-
...(params.host ? { host: params.host } : {}),
|
|
47
|
-
port: params.port ?? DEFAULT_PORT,
|
|
48
|
-
}, ctx, 'dev');
|
|
49
|
-
const handle = await runWranglerDev({
|
|
50
|
-
projectDir: ctx.projectDir,
|
|
51
|
-
configPath,
|
|
52
|
-
port: params.port ?? DEFAULT_PORT,
|
|
53
|
-
remote: false,
|
|
54
|
-
autoPickPort: params.port === undefined,
|
|
55
|
-
...(params.host ? { ip: '0.0.0.0' } : {}),
|
|
56
|
-
onReload: ctx.onReload,
|
|
57
|
-
onLog: logTo(),
|
|
58
|
-
});
|
|
59
|
-
return { url: params.host ?? handle.url, stop: handle.stop };
|
|
60
|
-
},
|
|
61
|
-
// Config hot-regen for the local dev loop: same codegen as `watch` (the
|
|
62
|
-
// running `wrangler dev` reloads its generated config + entry itself) —
|
|
63
|
-
// the params mapping must stay identical to `watch`'s prepare call.
|
|
64
|
-
async regenerate(params, ctx) {
|
|
65
|
-
await prepare({
|
|
66
|
-
...(params.vars ? { vars: params.vars } : {}),
|
|
67
|
-
...(params.host ? { host: params.host } : {}),
|
|
68
|
-
port: params.port ?? DEFAULT_PORT,
|
|
69
|
-
}, ctx, 'dev');
|
|
70
|
-
},
|
|
71
|
-
async deploy(params, ctx) {
|
|
72
|
-
const log = logTo();
|
|
73
|
-
if (!params.instance) {
|
|
74
|
-
throw new Error(`astrale adapter: this env has no "instance" — managed deploys need the target ` +
|
|
75
|
-
`instance slug (e.g. prod: { instance: '<your-instance-slug>' }).`);
|
|
76
|
-
}
|
|
77
|
-
// Build + pack the client SPA: managed services serve it under /ui (the
|
|
78
|
-
// platform stages the archive and the box downloads it at Service.init).
|
|
79
|
-
let assetsGz = null;
|
|
80
|
-
if (ctx.clientDir) {
|
|
81
|
-
await buildClient(ctx.clientDir, ctx.projectDir, log);
|
|
82
|
-
assetsGz = packAssets(join(ctx.projectDir, 'dist-client'));
|
|
83
|
-
if (assetsGz)
|
|
84
|
-
log(`packed client SPA (${assetsGz.length} bytes gz) — /ui ships managed`);
|
|
85
|
-
}
|
|
86
|
-
// 1. Codegen + bundle: the exact worker module a Cloudflare deploy would
|
|
87
|
-
// ship, produced locally with `wrangler deploy --dry-run` (no auth).
|
|
88
|
-
// `clientDir` is stripped: managed packages don't ship SPA assets yet,
|
|
89
|
-
// and leaving it in would put a dangling `assets.directory` (the
|
|
90
|
-
// unbuilt dist-client) into the generated config.
|
|
91
|
-
const { clientDir: _omitted, ...bundleCtx } = ctx;
|
|
92
|
-
const { configPath } = await prepare({}, bundleCtx, 'dev');
|
|
93
|
-
const outDir = join(ctx.projectDir, '.astrale', 'dist-managed');
|
|
94
|
-
const bundlePath = await runWranglerBundle({
|
|
95
|
-
projectDir: ctx.projectDir,
|
|
96
|
-
configPath,
|
|
97
|
-
outDir,
|
|
98
|
-
onLog: log,
|
|
99
|
-
});
|
|
100
|
-
const bundle = await readFile(bundlePath);
|
|
101
|
-
// 2. Publish through the platform, authenticated by the author's astrale
|
|
102
|
-
// CLI session. The release payload rides STDIN — a bundle is megabytes
|
|
103
|
-
// of base64, far past argv limits.
|
|
104
|
-
const name = params.name ?? packageNameFor(ctx.domain.origin);
|
|
105
|
-
const adminUrl = params.adminUrl ?? DEFAULT_ADMIN_URL;
|
|
106
|
-
const version = `0.0.0-managed.${Date.now()}`;
|
|
107
|
-
log(`registering "${name}" (${ctx.domain.origin}) in the platform catalog…`);
|
|
108
|
-
await astraleCall(ctx.projectDir, params, adminUrl, {
|
|
109
|
-
path: `/${ADMIN_ORIGIN}/class.DomainPublished/register`,
|
|
110
|
-
data: { origin: ctx.domain.origin, name },
|
|
111
|
-
});
|
|
112
|
-
log(`publishing ${name}@${version} (${bundle.length} bytes)…`);
|
|
113
|
-
await astraleCall(ctx.projectDir, params, adminUrl, {
|
|
114
|
-
path: `/admin/domains/${name}::release`,
|
|
115
|
-
data: {
|
|
116
|
-
version,
|
|
117
|
-
bundleBase64: bundle.toString('base64'),
|
|
118
|
-
makeCurrent: true,
|
|
119
|
-
...(assetsGz ? { assetsGzBase64: assetsGz.toString('base64') } : {}),
|
|
120
|
-
},
|
|
121
|
-
viaStdin: true,
|
|
122
|
-
});
|
|
123
|
-
// Author runtime secrets: read the env's dotenv locally, ship the map on
|
|
124
|
-
// the INSTALL call (never the release — secrets are per instance, never
|
|
125
|
-
// catalog-resident). Rides stdin so values never appear in argv.
|
|
126
|
-
let secrets;
|
|
127
|
-
if (params.secrets) {
|
|
128
|
-
secrets = loadDotenvFile(join(ctx.projectDir, params.secrets));
|
|
129
|
-
log(`shipping ${Object.keys(secrets).length} secret(s) from ${params.secrets}…`);
|
|
130
|
-
}
|
|
131
|
-
log(`installing on instance "${params.instance}"…`);
|
|
132
|
-
const installed = (await astraleCall(ctx.projectDir, params, adminUrl, {
|
|
133
|
-
path: `/admin/domains/${name}::install`,
|
|
134
|
-
data: {
|
|
135
|
-
instanceId: params.instance,
|
|
136
|
-
source: { kind: 'package' },
|
|
137
|
-
...(secrets ? { secrets } : {}),
|
|
138
|
-
},
|
|
139
|
-
viaStdin: true,
|
|
140
|
-
}));
|
|
141
|
-
if (installed.error || installed.state !== 'installed') {
|
|
142
|
-
throw new Error(`managed install failed: state=${installed.state ?? '?'} error=${installed.error ?? '?'}`);
|
|
143
|
-
}
|
|
144
|
-
if (!installed.serviceSlug) {
|
|
145
|
-
throw new Error('managed install returned no serviceSlug — cannot derive the service URL');
|
|
146
|
-
}
|
|
147
|
-
const region = params.region ?? DEFAULT_REGION;
|
|
148
|
-
const url = `https://${installed.serviceSlug}.svc.${region}.astrale.ai`;
|
|
149
|
-
return {
|
|
150
|
-
url,
|
|
151
|
-
// Managed deploys already installed on the instance — override the
|
|
152
|
-
// devkit's default "install on an instance" footer with the truth.
|
|
153
|
-
nextSteps: ` installed on "${params.instance}" — call it:\n` +
|
|
154
|
-
` astrale call "/${ctx.domain.origin}/..." -i ${params.instance}`,
|
|
155
|
-
};
|
|
156
|
-
},
|
|
157
|
-
secretsFile(params) {
|
|
158
|
-
return params.secrets;
|
|
159
|
-
},
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
/** Catalog package name from the origin: first DNS label (e.g. `my-notes.example.dev` → `my-notes`). */
|
|
163
|
-
function packageNameFor(origin) {
|
|
164
|
-
const label = origin.split('.')[0] ?? origin;
|
|
165
|
-
return label.toLowerCase().replace(/[^a-z0-9-]+/g, '-');
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* Invoke the user's `astrale` CLI for one platform call and parse its JSON
|
|
169
|
-
* output. The CLI owns identity (IdP session, audience scoping, delegation
|
|
170
|
-
* minting) — reimplementing that here would fork the trust path.
|
|
171
|
-
*/
|
|
172
|
-
async function astraleCall(projectDir, params, adminUrl, call) {
|
|
173
|
-
// Saga-sized timeout: a COLD managed install (fresh service on the host)
|
|
174
|
-
// runs runtime staging + serve probes and regularly exceeds the CLI's 30s
|
|
175
|
-
// default — which doesn't just fail the client, it can kill the saga
|
|
176
|
-
// mid-flight. Same budget as `astrale instance create`.
|
|
177
|
-
const args = ['call', call.path, '--url', adminUrl, '--json', '--timeout', '240000'];
|
|
178
|
-
if (params.identity)
|
|
179
|
-
args.push('--as', params.identity);
|
|
180
|
-
const payload = JSON.stringify(call.data);
|
|
181
|
-
if (!call.viaStdin)
|
|
182
|
-
args.push('--data', payload);
|
|
183
|
-
const { code, out } = await new Promise((resolve, reject) => {
|
|
184
|
-
const child = spawn(astraleBin(projectDir), args, {
|
|
185
|
-
cwd: projectDir,
|
|
186
|
-
stdio: [call.viaStdin ? 'pipe' : 'ignore', 'pipe', 'pipe'],
|
|
187
|
-
});
|
|
188
|
-
let out = '';
|
|
189
|
-
child.stdout?.on('data', (b) => (out += b.toString()));
|
|
190
|
-
child.stderr?.on('data', (b) => (out += b.toString()));
|
|
191
|
-
child.on('exit', (c) => resolve({ code: c ?? 1, out }));
|
|
192
|
-
child.on('error', reject);
|
|
193
|
-
if (call.viaStdin) {
|
|
194
|
-
child.stdin?.write(payload);
|
|
195
|
-
child.stdin?.end();
|
|
196
|
-
}
|
|
197
|
-
});
|
|
198
|
-
if (code !== 0) {
|
|
199
|
-
// Only suggest re-login for AUTH-shaped failures — a timeout or audience
|
|
200
|
-
// error pointed at "auth login" sends users chasing the wrong fix.
|
|
201
|
-
const authShaped = /AUTH_ERROR|expired|not signed in|credential/i.test(out);
|
|
202
|
-
throw new Error(`astrale call ${call.path} failed (exit ${code}): ${out.trim().slice(0, 500)}` +
|
|
203
|
-
(authShaped ? `\nIs the astrale CLI installed and signed in? (astrale auth login)` : ''));
|
|
204
|
-
}
|
|
205
|
-
try {
|
|
206
|
-
return JSON.parse(out);
|
|
207
|
-
}
|
|
208
|
-
catch {
|
|
209
|
-
throw new Error(`astrale call ${call.path}: could not parse CLI output: ${out.slice(0, 300)}`);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
/** The user's astrale CLI: ~/.astrale/bin/astrale when present, else PATH. */
|
|
213
|
-
function astraleBin(_projectDir) {
|
|
214
|
-
const home = process.env.HOME;
|
|
215
|
-
if (home) {
|
|
216
|
-
const installed = join(home, '.astrale', 'bin', 'astrale');
|
|
217
|
-
if (existsSync(installed))
|
|
218
|
-
return installed;
|
|
219
|
-
}
|
|
220
|
-
return 'astrale';
|
|
221
|
-
}
|
|
222
|
-
//# sourceMappingURL=astrale.js.map
|
package/dist/astrale.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"astrale.js","sourceRoot":"","sources":["../src/astrale.ts"],"names":[],"mappings":"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"}
|
package/src/astrale.ts
DELETED
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `astrale(envs)` — the Astrale-managed deployment adapter.
|
|
3
|
-
*
|
|
4
|
-
* Ships the domain THROUGH the platform instead of to the author's own cloud
|
|
5
|
-
* account: `deploy` builds the same single-module workerd bundle the Cloudflare
|
|
6
|
-
* adapter ships (shared codegen + `wrangler deploy --dry-run`), then publishes
|
|
7
|
-
* it via the admin domain's catalog — `DomainPublished.register` →
|
|
8
|
-
* `::release { bundleBase64 }` (the worker stages the bytes in the platform
|
|
9
|
-
* registry) → `::install { instanceId, source: { kind: 'package' } }`, which
|
|
10
|
-
* deploys it as a host-local Service next to the target instance and installs
|
|
11
|
-
* the domain on it. No Cloudflare account, no wrangler auth — the only
|
|
12
|
-
* credential is the author's Astrale identity, supplied by the `astrale` CLI
|
|
13
|
-
* session this adapter shells out to (the same trust source as
|
|
14
|
-
* `astrale instance install`).
|
|
15
|
-
*
|
|
16
|
-
* `watch` is identical to the Cloudflare adapter's local dev (wrangler dev on
|
|
17
|
-
* localhost) — managed deploys change WHERE the worker runs, not how you
|
|
18
|
-
* iterate locally.
|
|
19
|
-
*
|
|
20
|
-
* Managed deploys ship the client SPA (served under `/ui` by the box) and
|
|
21
|
-
* runtime secrets (dotenv map on the install call, encrypted at rest
|
|
22
|
-
* platform-side; platform-managed env keys always win precedence).
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
import type { DomainAdapter } from '@astrale-os/devkit'
|
|
26
|
-
|
|
27
|
-
import { defineAdapter, loadDotenvFile } from '@astrale-os/devkit'
|
|
28
|
-
import { spawn } from 'node:child_process'
|
|
29
|
-
import { existsSync } from 'node:fs'
|
|
30
|
-
import { readFile } from 'node:fs/promises'
|
|
31
|
-
import { join } from 'node:path'
|
|
32
|
-
|
|
33
|
-
import type { AstraleParams } from './params'
|
|
34
|
-
|
|
35
|
-
import { packAssets } from './assets-pack'
|
|
36
|
-
import { buildClient } from './client'
|
|
37
|
-
import { logTo, prepare } from './cloudflare'
|
|
38
|
-
import { runWranglerBundle, runWranglerDev } from './wrangler-cli'
|
|
39
|
-
|
|
40
|
-
const DEFAULT_PORT = 8787
|
|
41
|
-
const DEFAULT_ADMIN_URL = 'https://admin.eu.astrale.ai/api'
|
|
42
|
-
const DEFAULT_REGION = 'eu'
|
|
43
|
-
const ADMIN_ORIGIN = 'admin.astrale.ai'
|
|
44
|
-
|
|
45
|
-
export function astrale(envs: Record<string, AstraleParams>): DomainAdapter<AstraleParams> {
|
|
46
|
-
return defineAdapter<AstraleParams>({
|
|
47
|
-
name: 'astrale',
|
|
48
|
-
envs,
|
|
49
|
-
|
|
50
|
-
// Local dev is unchanged: wrangler dev on localhost. Managed deploys change
|
|
51
|
-
// WHERE the worker ships, not the inner loop.
|
|
52
|
-
async watch(params, ctx) {
|
|
53
|
-
const { configPath } = await prepare(
|
|
54
|
-
{
|
|
55
|
-
...(params.vars ? { vars: params.vars } : {}),
|
|
56
|
-
...(params.host ? { host: params.host } : {}),
|
|
57
|
-
port: params.port ?? DEFAULT_PORT,
|
|
58
|
-
},
|
|
59
|
-
ctx,
|
|
60
|
-
'dev',
|
|
61
|
-
)
|
|
62
|
-
const handle = await runWranglerDev({
|
|
63
|
-
projectDir: ctx.projectDir,
|
|
64
|
-
configPath,
|
|
65
|
-
port: params.port ?? DEFAULT_PORT,
|
|
66
|
-
remote: false,
|
|
67
|
-
autoPickPort: params.port === undefined,
|
|
68
|
-
...(params.host ? { ip: '0.0.0.0' } : {}),
|
|
69
|
-
onReload: ctx.onReload,
|
|
70
|
-
onLog: logTo(),
|
|
71
|
-
})
|
|
72
|
-
return { url: params.host ?? handle.url, stop: handle.stop }
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
// Config hot-regen for the local dev loop: same codegen as `watch` (the
|
|
76
|
-
// running `wrangler dev` reloads its generated config + entry itself) —
|
|
77
|
-
// the params mapping must stay identical to `watch`'s prepare call.
|
|
78
|
-
async regenerate(params, ctx) {
|
|
79
|
-
await prepare(
|
|
80
|
-
{
|
|
81
|
-
...(params.vars ? { vars: params.vars } : {}),
|
|
82
|
-
...(params.host ? { host: params.host } : {}),
|
|
83
|
-
port: params.port ?? DEFAULT_PORT,
|
|
84
|
-
},
|
|
85
|
-
ctx,
|
|
86
|
-
'dev',
|
|
87
|
-
)
|
|
88
|
-
},
|
|
89
|
-
|
|
90
|
-
async deploy(params, ctx) {
|
|
91
|
-
const log = logTo()
|
|
92
|
-
if (!params.instance) {
|
|
93
|
-
throw new Error(
|
|
94
|
-
`astrale adapter: this env has no "instance" — managed deploys need the target ` +
|
|
95
|
-
`instance slug (e.g. prod: { instance: '<your-instance-slug>' }).`,
|
|
96
|
-
)
|
|
97
|
-
}
|
|
98
|
-
// Build + pack the client SPA: managed services serve it under /ui (the
|
|
99
|
-
// platform stages the archive and the box downloads it at Service.init).
|
|
100
|
-
let assetsGz: Buffer | null = null
|
|
101
|
-
if (ctx.clientDir) {
|
|
102
|
-
await buildClient(ctx.clientDir, ctx.projectDir, log)
|
|
103
|
-
assetsGz = packAssets(join(ctx.projectDir, 'dist-client'))
|
|
104
|
-
if (assetsGz) log(`packed client SPA (${assetsGz.length} bytes gz) — /ui ships managed`)
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// 1. Codegen + bundle: the exact worker module a Cloudflare deploy would
|
|
108
|
-
// ship, produced locally with `wrangler deploy --dry-run` (no auth).
|
|
109
|
-
// `clientDir` is stripped: managed packages don't ship SPA assets yet,
|
|
110
|
-
// and leaving it in would put a dangling `assets.directory` (the
|
|
111
|
-
// unbuilt dist-client) into the generated config.
|
|
112
|
-
const { clientDir: _omitted, ...bundleCtx } = ctx
|
|
113
|
-
const { configPath } = await prepare({}, bundleCtx, 'dev')
|
|
114
|
-
const outDir = join(ctx.projectDir, '.astrale', 'dist-managed')
|
|
115
|
-
const bundlePath = await runWranglerBundle({
|
|
116
|
-
projectDir: ctx.projectDir,
|
|
117
|
-
configPath,
|
|
118
|
-
outDir,
|
|
119
|
-
onLog: log,
|
|
120
|
-
})
|
|
121
|
-
const bundle = await readFile(bundlePath)
|
|
122
|
-
|
|
123
|
-
// 2. Publish through the platform, authenticated by the author's astrale
|
|
124
|
-
// CLI session. The release payload rides STDIN — a bundle is megabytes
|
|
125
|
-
// of base64, far past argv limits.
|
|
126
|
-
const name = params.name ?? packageNameFor(ctx.domain.origin)
|
|
127
|
-
const adminUrl = params.adminUrl ?? DEFAULT_ADMIN_URL
|
|
128
|
-
const version = `0.0.0-managed.${Date.now()}`
|
|
129
|
-
|
|
130
|
-
log(`registering "${name}" (${ctx.domain.origin}) in the platform catalog…`)
|
|
131
|
-
await astraleCall(ctx.projectDir, params, adminUrl, {
|
|
132
|
-
path: `/${ADMIN_ORIGIN}/class.DomainPublished/register`,
|
|
133
|
-
data: { origin: ctx.domain.origin, name },
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
log(`publishing ${name}@${version} (${bundle.length} bytes)…`)
|
|
137
|
-
await astraleCall(ctx.projectDir, params, adminUrl, {
|
|
138
|
-
path: `/admin/domains/${name}::release`,
|
|
139
|
-
data: {
|
|
140
|
-
version,
|
|
141
|
-
bundleBase64: bundle.toString('base64'),
|
|
142
|
-
makeCurrent: true,
|
|
143
|
-
...(assetsGz ? { assetsGzBase64: assetsGz.toString('base64') } : {}),
|
|
144
|
-
},
|
|
145
|
-
viaStdin: true,
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
// Author runtime secrets: read the env's dotenv locally, ship the map on
|
|
149
|
-
// the INSTALL call (never the release — secrets are per instance, never
|
|
150
|
-
// catalog-resident). Rides stdin so values never appear in argv.
|
|
151
|
-
let secrets: Record<string, string> | undefined
|
|
152
|
-
if (params.secrets) {
|
|
153
|
-
secrets = loadDotenvFile(join(ctx.projectDir, params.secrets))
|
|
154
|
-
log(`shipping ${Object.keys(secrets).length} secret(s) from ${params.secrets}…`)
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
log(`installing on instance "${params.instance}"…`)
|
|
158
|
-
const installed = (await astraleCall(ctx.projectDir, params, adminUrl, {
|
|
159
|
-
path: `/admin/domains/${name}::install`,
|
|
160
|
-
data: {
|
|
161
|
-
instanceId: params.instance,
|
|
162
|
-
source: { kind: 'package' },
|
|
163
|
-
...(secrets ? { secrets } : {}),
|
|
164
|
-
},
|
|
165
|
-
viaStdin: true,
|
|
166
|
-
})) as { serviceSlug?: string; state?: string; error?: string | null }
|
|
167
|
-
|
|
168
|
-
if (installed.error || installed.state !== 'installed') {
|
|
169
|
-
throw new Error(
|
|
170
|
-
`managed install failed: state=${installed.state ?? '?'} error=${installed.error ?? '?'}`,
|
|
171
|
-
)
|
|
172
|
-
}
|
|
173
|
-
if (!installed.serviceSlug) {
|
|
174
|
-
throw new Error('managed install returned no serviceSlug — cannot derive the service URL')
|
|
175
|
-
}
|
|
176
|
-
const region = params.region ?? DEFAULT_REGION
|
|
177
|
-
const url = `https://${installed.serviceSlug}.svc.${region}.astrale.ai`
|
|
178
|
-
return {
|
|
179
|
-
url,
|
|
180
|
-
// Managed deploys already installed on the instance — override the
|
|
181
|
-
// devkit's default "install on an instance" footer with the truth.
|
|
182
|
-
nextSteps:
|
|
183
|
-
` installed on "${params.instance}" — call it:\n` +
|
|
184
|
-
` astrale call "/${ctx.domain.origin}/..." -i ${params.instance}`,
|
|
185
|
-
}
|
|
186
|
-
},
|
|
187
|
-
|
|
188
|
-
secretsFile(params) {
|
|
189
|
-
return params.secrets
|
|
190
|
-
},
|
|
191
|
-
})
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/** Catalog package name from the origin: first DNS label (e.g. `my-notes.example.dev` → `my-notes`). */
|
|
195
|
-
function packageNameFor(origin: string): string {
|
|
196
|
-
const label = origin.split('.')[0] ?? origin
|
|
197
|
-
return label.toLowerCase().replace(/[^a-z0-9-]+/g, '-')
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Invoke the user's `astrale` CLI for one platform call and parse its JSON
|
|
202
|
-
* output. The CLI owns identity (IdP session, audience scoping, delegation
|
|
203
|
-
* minting) — reimplementing that here would fork the trust path.
|
|
204
|
-
*/
|
|
205
|
-
async function astraleCall(
|
|
206
|
-
projectDir: string,
|
|
207
|
-
params: AstraleParams,
|
|
208
|
-
adminUrl: string,
|
|
209
|
-
call: { path: string; data: unknown; viaStdin?: boolean },
|
|
210
|
-
): Promise<unknown> {
|
|
211
|
-
// Saga-sized timeout: a COLD managed install (fresh service on the host)
|
|
212
|
-
// runs runtime staging + serve probes and regularly exceeds the CLI's 30s
|
|
213
|
-
// default — which doesn't just fail the client, it can kill the saga
|
|
214
|
-
// mid-flight. Same budget as `astrale instance create`.
|
|
215
|
-
const args = ['call', call.path, '--url', adminUrl, '--json', '--timeout', '240000']
|
|
216
|
-
if (params.identity) args.push('--as', params.identity)
|
|
217
|
-
const payload = JSON.stringify(call.data)
|
|
218
|
-
if (!call.viaStdin) args.push('--data', payload)
|
|
219
|
-
|
|
220
|
-
const { code, out } = await new Promise<{ code: number; out: string }>((resolve, reject) => {
|
|
221
|
-
const child = spawn(astraleBin(projectDir), args, {
|
|
222
|
-
cwd: projectDir,
|
|
223
|
-
stdio: [call.viaStdin ? 'pipe' : 'ignore', 'pipe', 'pipe'],
|
|
224
|
-
})
|
|
225
|
-
let out = ''
|
|
226
|
-
child.stdout?.on('data', (b: Buffer) => (out += b.toString()))
|
|
227
|
-
child.stderr?.on('data', (b: Buffer) => (out += b.toString()))
|
|
228
|
-
child.on('exit', (c) => resolve({ code: c ?? 1, out }))
|
|
229
|
-
child.on('error', reject)
|
|
230
|
-
if (call.viaStdin) {
|
|
231
|
-
child.stdin?.write(payload)
|
|
232
|
-
child.stdin?.end()
|
|
233
|
-
}
|
|
234
|
-
})
|
|
235
|
-
if (code !== 0) {
|
|
236
|
-
// Only suggest re-login for AUTH-shaped failures — a timeout or audience
|
|
237
|
-
// error pointed at "auth login" sends users chasing the wrong fix.
|
|
238
|
-
const authShaped = /AUTH_ERROR|expired|not signed in|credential/i.test(out)
|
|
239
|
-
throw new Error(
|
|
240
|
-
`astrale call ${call.path} failed (exit ${code}): ${out.trim().slice(0, 500)}` +
|
|
241
|
-
(authShaped ? `\nIs the astrale CLI installed and signed in? (astrale auth login)` : ''),
|
|
242
|
-
)
|
|
243
|
-
}
|
|
244
|
-
try {
|
|
245
|
-
return JSON.parse(out)
|
|
246
|
-
} catch {
|
|
247
|
-
throw new Error(`astrale call ${call.path}: could not parse CLI output: ${out.slice(0, 300)}`)
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/** The user's astrale CLI: ~/.astrale/bin/astrale when present, else PATH. */
|
|
252
|
-
function astraleBin(_projectDir: string): string {
|
|
253
|
-
const home = process.env.HOME
|
|
254
|
-
if (home) {
|
|
255
|
-
const installed = join(home, '.astrale', 'bin', 'astrale')
|
|
256
|
-
if (existsSync(installed)) return installed
|
|
257
|
-
}
|
|
258
|
-
return 'astrale'
|
|
259
|
-
}
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Minimal, self-contained kernel JSON client — just enough to load a node via
|
|
3
|
-
* `@<id>::get`, without pulling in `@astrale-os/kernel-client` (and its
|
|
4
|
-
* transitive deps). It reproduces the kernel ENVELOPE wire shape inline.
|
|
5
|
-
*
|
|
6
|
-
* Wire contract (authoritative source: `kernel/api/envelope/`):
|
|
7
|
-
* - Request: POST <kernelUrl> with headers
|
|
8
|
-
* content-type: application/vnd.astrale.kernel+json
|
|
9
|
-
* accept: application/vnd.astrale.kernel+json
|
|
10
|
-
* authorization: <delegationToken> (BARE token — no "Bearer " prefix)
|
|
11
|
-
* body JSON `{ method, params, id }` (see `encode.ts:encodeKernelRequest`).
|
|
12
|
-
* - Response: JSON, exactly one of (decode precedence error → redirect → result,
|
|
13
|
-
* mirroring `decode.ts:decodeKernelResponse`):
|
|
14
|
-
* { error: { code, message }, id } → throw Error("<code>: <message>")
|
|
15
|
-
* { redirect, id } → throw (we don't follow redirects here)
|
|
16
|
-
* { result, id } → return result
|
|
17
|
-
*
|
|
18
|
-
* Deliberately JSON-only: no msgpack codec, no streaming/binary, no redirect
|
|
19
|
-
* following, no schema/batching. Those live in `@astrale-os/kernel-client`,
|
|
20
|
-
* which this self-contained build omits on purpose.
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
// From `kernel/api/envelope/types.ts` (KERNEL_CONTENT_TYPE_JSON). Inlined so the
|
|
24
|
-
// template needs no @astrale-os import.
|
|
25
|
-
const KERNEL_CONTENT_TYPE_JSON = 'application/vnd.astrale.kernel+json'
|
|
26
|
-
|
|
27
|
-
// Module-local monotonic request id. Strings keep ids stable across reloads and
|
|
28
|
-
// distinguishable in logs; the kernel echoes `id` back but we don't correlate
|
|
29
|
-
// (one request per fetch).
|
|
30
|
-
let idSeq = 0
|
|
31
|
-
function nextId(): string {
|
|
32
|
-
idSeq += 1
|
|
33
|
-
return `c${idSeq}`
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Prop key constants — the graph stores props with fully-qualified keys
|
|
38
|
-
* (`<domain>:<member>.property.<name>`). The kernel `Named.name` key is fixed
|
|
39
|
-
* and known; domain props (`title`, `body`) are qualified by the (build-time
|
|
40
|
-
* unknown) domain origin — read those by suffix with `readPropBySuffix`.
|
|
41
|
-
*/
|
|
42
|
-
export const PROP = {
|
|
43
|
-
named: {
|
|
44
|
-
name: 'kernel.astrale.ai:interface.Named.property.name',
|
|
45
|
-
},
|
|
46
|
-
} as const
|
|
47
|
-
|
|
48
|
-
export type KernelNode = {
|
|
49
|
-
id: string
|
|
50
|
-
path: string
|
|
51
|
-
class: string | { raw?: string }
|
|
52
|
-
props: Record<string, unknown>
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export function readProp(props: Record<string, unknown>, key: string): string | undefined {
|
|
56
|
-
const v = props[key]
|
|
57
|
-
return typeof v === 'string' ? v : undefined
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Read a string prop by key suffix — for domain-qualified props whose full key
|
|
62
|
-
* embeds the (build-time-unknown) domain origin. e.g.
|
|
63
|
-
* `readPropBySuffix(props, '.property.body')`.
|
|
64
|
-
*/
|
|
65
|
-
export function readPropBySuffix(
|
|
66
|
-
props: Record<string, unknown>,
|
|
67
|
-
suffix: string,
|
|
68
|
-
): string | undefined {
|
|
69
|
-
for (const [k, v] of Object.entries(props)) {
|
|
70
|
-
if (k.endsWith(suffix) && typeof v === 'string') return v
|
|
71
|
-
}
|
|
72
|
-
return undefined
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/** Short class name from a `class.raw` path `/:<domain>:class.<Name>` → `<Name>`. */
|
|
76
|
-
export function classShortName(node: KernelNode): string {
|
|
77
|
-
const raw = (typeof node.class === 'string' ? node.class : node.class?.raw) ?? ''
|
|
78
|
-
const last = raw.split(':').pop() ?? ''
|
|
79
|
-
const dot = last.indexOf('.')
|
|
80
|
-
return dot >= 0 ? last.slice(dot + 1) : last
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* POST a single kernel call and return its `result`. Throws on a kernel error
|
|
85
|
-
* envelope or an unexpected redirect. `token` is the bare delegation credential
|
|
86
|
-
* from the shell handshake; the iframe authenticates SOLELY with it (the parent
|
|
87
|
-
* minted it for this kernel, so the audience already matches — no cookie/mint).
|
|
88
|
-
*/
|
|
89
|
-
export async function kernelCall(
|
|
90
|
-
kernelUrl: string,
|
|
91
|
-
token: string,
|
|
92
|
-
method: string,
|
|
93
|
-
params: Record<string, unknown> = {},
|
|
94
|
-
): Promise<unknown> {
|
|
95
|
-
// The kernel routes on a trailing slash; the parent absolutizes the URL, but
|
|
96
|
-
// not always with the slash, so normalize here.
|
|
97
|
-
const url = kernelUrl.endsWith('/') ? kernelUrl : `${kernelUrl}/`
|
|
98
|
-
const id = nextId()
|
|
99
|
-
|
|
100
|
-
const res = await fetch(url, {
|
|
101
|
-
method: 'POST',
|
|
102
|
-
headers: {
|
|
103
|
-
'content-type': KERNEL_CONTENT_TYPE_JSON,
|
|
104
|
-
accept: KERNEL_CONTENT_TYPE_JSON,
|
|
105
|
-
authorization: token,
|
|
106
|
-
},
|
|
107
|
-
body: JSON.stringify({ method, params, id }),
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
let body: unknown
|
|
111
|
-
try {
|
|
112
|
-
body = await res.json()
|
|
113
|
-
} catch {
|
|
114
|
-
throw new Error(`kernel returned a non-JSON response (HTTP ${res.status})`)
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (body === null || typeof body !== 'object') {
|
|
118
|
-
throw new Error(`kernel returned an unexpected response (HTTP ${res.status})`)
|
|
119
|
-
}
|
|
120
|
-
const obj = body as Record<string, unknown>
|
|
121
|
-
|
|
122
|
-
// Decode precedence error → redirect → result (mirrors decodeKernelResponse).
|
|
123
|
-
if ('error' in obj && obj.error && typeof obj.error === 'object') {
|
|
124
|
-
const err = obj.error as { code?: unknown; message?: unknown }
|
|
125
|
-
const code = typeof err.code === 'number' ? err.code : 5000
|
|
126
|
-
const message = typeof err.message === 'string' ? err.message : 'Unknown error'
|
|
127
|
-
throw new Error(`${code}: ${message}`)
|
|
128
|
-
}
|
|
129
|
-
if ('redirect' in obj && obj.redirect) {
|
|
130
|
-
// Redirects (remote-domain Functions) aren't followed by this minimal
|
|
131
|
-
// client — they require credential re-minting against the target worker.
|
|
132
|
-
throw new Error('unexpected redirect from kernel (not supported by the template client)')
|
|
133
|
-
}
|
|
134
|
-
return obj.result
|
|
135
|
-
}
|