@astrale-os/sdk 0.6.0-beta.19 → 0.6.0-beta.20

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.
@@ -1,18 +1,23 @@
1
1
  import type { DeploymentSummaryV1 } from '../../address/index.js';
2
2
  /**
3
- * Where one listed deployment is called on its host, so that an operator can act on that
4
- * deployment alone, such as rotating one of its secrets in place (AM-4'):
3
+ * Where one listed deployment is called on its host, so that an operator can act on it:
5
4
  *
6
5
  * - `services`: a deployment Admin's Services hosts, called on the Admin instance with
7
- * `astrale call "<path>.method.setSecret" --admin` (`--admin=<admin>` when `admin` names the
8
- * Admin bookmark). `path` is the deployment's node, `@<id>`, qualified by its class:
9
- * `@<id>::services.astrale.ai:class.CloudflareDeployment`.
6
+ * `astrale call "<node>.method.<method>" --admin` (`--admin=<admin>` when `admin` names the
7
+ * Admin bookmark). `path` is the deployment's node, `@<id>`, qualified by its class,
8
+ * `@<id>::services.astrale.ai:class.CloudflareDeployment`, whose only Method is `retire`.
9
+ * `service` is the node of the Service it is a version of,
10
+ * `@<serviceId>::services.astrale.ai:class.CloudflareService`, whose Methods act on the Service
11
+ * and the deployment it currently serves: `promote` (this deployment's label rolls the Service
12
+ * back or forward to it), `setSecret` and `secrets`, `setSchedule` and `schedules`, `logs`,
13
+ * `transfer` and `delete`.
10
14
  * - `namespace`: a deployment of a platform dispatch namespace, which has no Services node: the
11
15
  * namespace and the script the per-script tooling of that namespace takes.
12
16
  */
13
17
  export type DeploymentCallTarget = {
14
18
  readonly kind: 'services';
15
19
  readonly path: string;
20
+ readonly service: string;
16
21
  /** The Admin bookmark the deployment is reached through; the CLI's own Admin target when absent. */
17
22
  readonly admin?: string;
18
23
  } | {
@@ -25,6 +30,8 @@ export type DeploymentCallTarget = {
25
30
  * host stores it, and where it is called. `lastCallAt`, and a preview's `expiresAt`, are as of the
26
31
  * listing: an adapter whose host records calls only now and then reads the calls counted since, and
27
32
  * one that cannot read them for want of a permission omits both rather than print stale values.
33
+ * Admin's Services keeps every deployment until it is retired and counts no call: its deployments
34
+ * are `published`, `withdrawn` once retiring, and carry neither clock.
28
35
  *
29
36
  * The adapter does not admit the record. The listing admits it with `acceptDeploymentRecord`, and a
30
37
  * record it refuses (one a host on an older SDK still admits, say) never fails the listing: that
@@ -37,5 +44,10 @@ export interface ListedDeployment extends Omit<DeploymentSummaryV1, 'record'> {
37
44
  * it activates the deployment.
38
45
  */
39
46
  readonly record?: unknown;
47
+ /**
48
+ * Whether the stable URL of the deployment's Service serves it. Present only where the host has
49
+ * Services (Admin's Services); a platform namespace has none.
50
+ */
51
+ readonly current?: boolean;
40
52
  readonly callTarget: DeploymentCallTarget;
41
53
  }
@@ -4,10 +4,6 @@ import { ENVIRONMENT_NAME } from '../deployment/environment.js';
4
4
  import { isTests } from '../testing/index.js';
5
5
  import { installCommand } from './install-command.js';
6
6
  const admittedProjects = new WeakSet();
7
- /** The adapter name of adapter-astrale, whose removed instance mode once read `installation`. */
8
- const ASTRALE_ADAPTER = 'astrale';
9
- /** The one Environment adapter-astrale deploys without an explicit organisation (AM-7). */
10
- const ASTRALE_DEFAULT_ORGANIZATION_ENVIRONMENT = 'development';
11
7
  /** Capture one inert Project definition without loading entrypoints or performing effects. */
12
8
  export function defineProject(input) {
13
9
  const value = record(input, 'Project definition');
@@ -82,10 +78,8 @@ function admitEntrypoints(input) {
82
78
  * `installation` is refused before any effect, with the commands that replace it. Its only
83
79
  * consumer pins an SDK older than this refusal.
84
80
  *
85
- * adapter-astrale deploys on Admin's Services, never through the installation's instance: outside
86
- * development its Environment also names the organisation that holds its deployment line (AM-7),
87
- * which the guidance adds when the adapter names none. The installation's instance and identity
88
- * move to the install command.
81
+ * adapter-astrale deploys on Admin's Services, never through the installation's instance. The
82
+ * installation's instance and identity move to the install command.
89
83
  */
90
84
  function installationRefusal(environment, name) {
91
85
  const declared = environment.installation;
@@ -98,15 +92,8 @@ function installationRefusal(environment, name) {
98
92
  : {};
99
93
  const instance = stable(installation.instance);
100
94
  const identity = stable(installation.identity);
101
- const deployment = environment.deployment;
102
- const organization = isDeployment(deployment) &&
103
- deployment.adapter.name === ASTRALE_ADAPTER &&
104
- name !== ASTRALE_DEFAULT_ORGANIZATION_ENVIRONMENT &&
105
- deployment.parameters.organization === undefined
106
- ? ", name the organisation that holds its deployment line in `astrale({ organization: '<Identity id>' })`"
107
- : '';
108
95
  return (`Project Environment ${name} declares installation, and a deploy no longer installs. ` +
109
- `Remove \`installation\`${organization}, run \`astrale-domain deploy ${name}\`, ` +
96
+ `Remove \`installation\`, run \`astrale-domain deploy ${name}\`, ` +
110
97
  `then install the URL it prints with \`${installCommand(instance, identity)}\`.`);
111
98
  }
112
99
  /** A declared coordinate worth repeating in guidance: a non-empty single-line trimmed string. */
@@ -2,8 +2,6 @@ import { installCommand } from '../../project/install-command.js';
2
2
  /** The adapter names of the first-party adapters whose parameters CT33 classifies. */
3
3
  const CLOUDFLARE = 'cloudflare';
4
4
  const ASTRALE = 'astrale';
5
- /** The one Environment adapter-astrale deploys without an explicit organisation (AM-7). */
6
- const DEVELOPMENT = 'development';
7
5
  /** The parameters that named the stable Service of adapter-astrale's removed instance mode. */
8
6
  const ASTRALE_SERVICE_PARAMETERS = ['serviceKey', 'name'];
9
7
  const CLOUDFLARE_DIRECT_PARAMETERS = [
@@ -48,10 +46,7 @@ export function selectDeployMode(input) {
48
46
  refusals.push({
49
47
  reason: 'astrale-signing-identity',
50
48
  guidance: 'astrale({ signingIdentity }) is no longer accepted: each deployment gets a signing key ' +
51
- 'generated when it is deployed. Remove `signingIdentity`' +
52
- // With `instance`, its own refusal already names the organisation.
53
- (declares(parameters, 'instance') ? '' : organizationGuidance(parameters, environment)) +
54
- '.',
49
+ 'generated when it is deployed. Remove `signingIdentity`.',
55
50
  });
56
51
  }
57
52
  }
@@ -98,8 +93,8 @@ export function selectDeployMode(input) {
98
93
  }
99
94
  /**
100
95
  * How an adapter-astrale Environment of the removed instance mode deploys now: without `instance`
101
- * (nor `serviceKey` and `name`, which named its stable Service), outside development on a line its
102
- * named organisation holds (AM-7), then installed by URL on the instance it used to deploy through.
96
+ * (nor `serviceKey` and `name`, which named its stable Service), then installed by URL on the
97
+ * instance it used to deploy through.
103
98
  */
104
99
  function astraleInstanceGuidance(parameters, environment) {
105
100
  const instance = String(parameters.instance);
@@ -108,18 +103,11 @@ function astraleInstanceGuidance(parameters, environment) {
108
103
  ? 'Remove `instance`'
109
104
  : `Remove \`instance\` with ${service.map((name) => `\`${name}\``).join(' and ')}, ` +
110
105
  `which named its stable Service`;
111
- const organization = organizationGuidance(parameters, environment);
112
106
  return (`astrale({ instance: '${instance}' }) deployed through that instance's Services, which no ` +
113
- `longer hosts deployments. ${removed}${organization}: \`astrale-domain deploy ${environment}\` ` +
107
+ `longer hosts deployments. ${removed}: \`astrale-domain deploy ${environment}\` ` +
114
108
  `makes an immutable deployment on Admin's Services, and \`${installCommand(instance)}\` ` +
115
109
  'installs it.');
116
110
  }
117
- /** Outside development, an adapter-astrale Environment names the organisation of its line (AM-7). */
118
- function organizationGuidance(parameters, environment) {
119
- return environment === DEVELOPMENT || declares(parameters, 'organization')
120
- ? ''
121
- : ", and name the organisation that holds the deployment line in `astrale({ organization: '<Identity id>' })`";
122
- }
123
111
  /**
124
112
  * Why a legacy direct-mode Environment cannot be published, and the configuration that makes it
125
113
  * publishable: a version names an immutable deployment's release, which a stable target never
@@ -27,9 +27,10 @@ export interface ListResultV1 {
27
27
  /**
28
28
  * One deployment: what its host knows (`DeploymentSummaryV1`), the Environment it was listed for
29
29
  * and its adapter, its computed name, and its call target. `lastCallAt` and a preview's `expiresAt`
30
- * are as of the listing: an adapter reads the calls its host counted since it last recorded them
31
- * (adapter-astrale: `CloudflareDeployment.lastCalls`), or every call from the calls dataset of a
32
- * platform namespace (adapter-cloudflare), which omits both without Account Analytics Read.
30
+ * are as of the listing: a platform namespace (adapter-cloudflare) reads every call from its calls
31
+ * dataset, and omits both without Account Analytics Read. Admin's Services (adapter-astrale) keeps
32
+ * every deployment until it is retired and counts no call: its rows are `published`, with neither
33
+ * clock, and say whether their Service serves them (`current`).
33
34
  */
34
35
  export type ListedDeploymentV1 = Omit<DeploymentSummaryV1, 'record'> & {
35
36
  /** The Project Environment whose line holds the deployment, as its host listed it. */
@@ -64,7 +65,12 @@ export type ListedDeploymentV1 = Omit<DeploymentSummaryV1, 'record'> & {
64
65
  * present exactly when `record` is `null`.
65
66
  */
66
67
  readonly recordRefusal?: string;
67
- /** Where to call this deployment alone, such as to rotate one of its secrets in place. */
68
+ /**
69
+ * Whether the stable URL of the deployment's Service serves it; present only for a host with
70
+ * Services (Admin's Services).
71
+ */
72
+ readonly current?: boolean;
73
+ /** Where to call this deployment, and on Admin's Services its Service. */
68
74
  readonly callTarget: DeploymentCallTarget;
69
75
  };
70
76
  /**
@@ -111,8 +117,8 @@ export type DeploymentRegistry = Pick<DomainRegistry, 'index' | 'instance'>;
111
117
  * Only an Environment that deploys immutable deployments (CT33 canonical) is listed; the others are
112
118
  * `unlisted`, with the reason. A record the host returns but `acceptDeploymentRecord` refuses never
113
119
  * fails the listing: that deployment is listed from what its host knows, its record `null`. This
114
- * covers a host whose SDK still admits a record this SDK refuses; a host that refuses a stored
115
- * record itself may fail its own list (Admin's Services does, AM-152). An Environment whose host
120
+ * covers a host whose SDK still admits a record this SDK refuses, and a host that lists a record
121
+ * its own SDK no longer admits as it is stored (Admin's Services does). An Environment whose host
116
122
  * cannot be listed, or whose adapter answers anything but deployments, is a failure, and the others
117
123
  * are still listed.
118
124
  *
@@ -140,9 +146,9 @@ export declare function listDeployments(input: {
140
146
  }): Promise<DeploymentListing>;
141
147
  /**
142
148
  * The listing as a table for a person, at `now`: each deployment's release, its name as computed
143
- * (never shortened, AM-57), its Environment and state, the instances known to pin it (`?` when a
144
- * source could not tell, AM-198), its last call and its expiry, and the URL `astrale domain
145
- * install` takes. Without deployments, it says so only for what was listed: nothing when a host
149
+ * (never shortened, AM-57), its Environment and state (`active (current)` for the deployment its
150
+ * Service serves), the instances known to pin it (`?` when a source could not tell, AM-198), its
151
+ * last call and its expiry, and the URL `astrale domain install` takes. Without deployments, it says so only for what was listed: nothing when a host
146
152
  * `failed` (stderr says which), and that some Environments were not listed when any was not.
147
153
  */
148
154
  export declare function renderDeploymentList(result: ListResultV1, now: number, failed?: boolean): readonly string[];
@@ -10,8 +10,8 @@ import { installationNotices, installedOn, installedOnCell, readInstallations, s
10
10
  * Only an Environment that deploys immutable deployments (CT33 canonical) is listed; the others are
11
11
  * `unlisted`, with the reason. A record the host returns but `acceptDeploymentRecord` refuses never
12
12
  * fails the listing: that deployment is listed from what its host knows, its record `null`. This
13
- * covers a host whose SDK still admits a record this SDK refuses; a host that refuses a stored
14
- * record itself may fail its own list (Admin's Services does, AM-152). An Environment whose host
13
+ * covers a host whose SDK still admits a record this SDK refuses, and a host that lists a record
14
+ * its own SDK no longer admits as it is stored (Admin's Services does). An Environment whose host
15
15
  * cannot be listed, or whose adapter answers anything but deployments, is a failure, and the others
16
16
  * are still listed.
17
17
  *
@@ -198,6 +198,7 @@ function listedDeployment(hosted, publications, installations) {
198
198
  signingKeyId: deployment.signingKeyId,
199
199
  record,
200
200
  ...(recordRefusal === undefined ? {} : { recordRefusal }),
201
+ ...(deployment.current === undefined ? {} : { current: deployment.current }),
201
202
  callTarget: deployment.callTarget,
202
203
  });
203
204
  }
@@ -275,9 +276,13 @@ function admitListed(deployment, adapter) {
275
276
  value.retirement !== 'withdrawn') {
276
277
  invalid('retirement');
277
278
  }
279
+ if (value.current !== undefined && typeof value.current !== 'boolean')
280
+ invalid('current');
278
281
  const target = members(value.callTarget) ?? invalid('callTarget');
279
282
  if (target.kind === 'services') {
280
- if (!text(target.path) || (target.admin !== undefined && !text(target.admin))) {
283
+ if (!text(target.path) ||
284
+ !text(target.service) ||
285
+ (target.admin !== undefined && !text(target.admin))) {
281
286
  invalid('callTarget');
282
287
  }
283
288
  }
@@ -308,9 +313,9 @@ const HOUR = 60 * MINUTE;
308
313
  const DAY = 24 * HOUR;
309
314
  /**
310
315
  * The listing as a table for a person, at `now`: each deployment's release, its name as computed
311
- * (never shortened, AM-57), its Environment and state, the instances known to pin it (`?` when a
312
- * source could not tell, AM-198), its last call and its expiry, and the URL `astrale domain
313
- * install` takes. Without deployments, it says so only for what was listed: nothing when a host
316
+ * (never shortened, AM-57), its Environment and state (`active (current)` for the deployment its
317
+ * Service serves), the instances known to pin it (`?` when a source could not tell, AM-198), its
318
+ * last call and its expiry, and the URL `astrale domain install` takes. Without deployments, it says so only for what was listed: nothing when a host
314
319
  * `failed` (stderr says which), and that some Environments were not listed when any was not.
315
320
  */
316
321
  export function renderDeploymentList(result, now, failed = false) {
@@ -328,7 +333,7 @@ export function renderDeploymentList(result, now, failed = false) {
328
333
  deployment.record === null ? '?' : shortDigest(deployment.record.releaseDigest),
329
334
  deployment.name,
330
335
  deployment.environment,
331
- deployment.state,
336
+ deployment.current === true ? `${deployment.state} (current)` : deployment.state,
332
337
  installedOnCell(deployment.installedOn, result.installations),
333
338
  deployment.lastCallAt === undefined
334
339
  ? '—'
@@ -299,8 +299,8 @@ function described(cause) {
299
299
  }
300
300
  /**
301
301
  * Keep the published deployment where its host has no registry principal to mark it: the adapter
302
- * declares `retain` for the platform namespace only (CT36). Admin marks the deployments of its
303
- * Services itself (A7), so `publish` never calls Services' `retain`. A `RetainRefusal` (absent or
302
+ * declares `retain` for the platform namespace only (CT36). Admin's Services keeps every
303
+ * deployment until it is retired, so adapter-astrale declares none. A `RetainRefusal` (absent or
304
304
  * retired) is `refused`, which no rerun changes; any other rejection `failed`, which a rerun
305
305
  * retries.
306
306
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrale-os/sdk",
3
- "version": "0.6.0-beta.19",
3
+ "version": "0.6.0-beta.20",
4
4
  "description": "Schema-first SDK for defining, composing, and deploying Astrale domains",
5
5
  "keywords": [
6
6
  "astrale",