@astrale-os/cli 1.0.0-beta.0 → 1.0.0-beta.1

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/astrale.js CHANGED
@@ -9738,7 +9738,7 @@ var package_default;
9738
9738
  var init_package = __esm(() => {
9739
9739
  package_default = {
9740
9740
  name: "@astrale-os/cli",
9741
- version: "1.0.0-beta.0",
9741
+ version: "1.0.0-beta.1",
9742
9742
  description: "Astrale CLI — connect to existing Astrale kernels",
9743
9743
  keywords: [
9744
9744
  "astrale",
@@ -86817,10 +86817,6 @@ async function connectAdminInstances(context, dependencies = {}) {
86817
86817
  throw new AdminHostNotFoundError(hostId);
86818
86818
  return instanceFromSummary(await binding6.$.invoke(Host.$.method("createInstance"), Path.parse(host.path), input));
86819
86819
  }
86820
- const inventory = await hostInventory(context.graph, binding6, Host, fleet);
86821
- const eligible = inventory.hosts.filter((host) => host.state === "ready" && host.path !== inventory.reserved);
86822
- if (eligible.length > 1)
86823
- throw hostSelectionRequired(eligible.map((host) => host.id));
86824
86820
  return instanceFromSummary(await binding6.$.invoke(Fleet.$.method("createInstance"), fleet, input));
86825
86821
  },
86826
86822
  status: (identifier) => invokeInstance("status", identifier),
@@ -86864,9 +86860,6 @@ async function hostInventory(graph, binding6, Host, fleet) {
86864
86860
  ...reserved === undefined ? {} : { reserved }
86865
86861
  });
86866
86862
  }
86867
- function hostSelectionRequired(ids2) {
86868
- return new Error(`alphaCreate could not choose a host: ${ids2.length} ready hosts are assigned (${[...ids2].sort().join(", ")}). ` + "Specify host_id once multi-host placement is enabled.");
86869
- }
86870
86863
  function instanceFromSummary(input) {
86871
86864
  const value2 = record9(input, "Admin Instance summary");
86872
86865
  const failure = value2.failure === undefined ? undefined : record9(value2.failure, "Admin failure");
@@ -89779,7 +89772,9 @@ var init_update = __esm(() => {
89779
89772
  // src/commands/update.ts
89780
89773
  var exports_update = {};
89781
89774
  __export(exports_update, {
89782
- default: () => update_default
89775
+ cliStale: () => cliStale,
89776
+ default: () => update_default,
89777
+ fetchNpmTargetVersion: () => fetchNpmTargetVersion
89783
89778
  });
89784
89779
  async function refreshSkills() {
89785
89780
  if (!detectSkill(ASTRALE_CLI_SKILL).installed) {
@@ -89828,29 +89823,35 @@ async function refreshSdkDeps(check3, assumeYes = false) {
89828
89823
  }
89829
89824
  return true;
89830
89825
  }
89831
- async function cliStale(opts) {
89826
+ async function cliStale(opts, dependencies = CLI_STALE_DEPENDENCIES) {
89832
89827
  const running = package_default.version;
89833
- const latest = await fetchNpmLatestVersion().catch(() => {
89834
- return;
89835
- });
89836
89828
  try {
89837
- const r = await updateAstrale({
89829
+ const r = await dependencies.update({
89838
89830
  check: true,
89839
89831
  channel: opts.channel,
89840
89832
  version: opts.version,
89841
89833
  currentVersion: running
89842
89834
  });
89843
89835
  if (r.status === "updated") {
89844
- return { stale: false, managed: false, current: running, latest: latest ?? running };
89836
+ return {
89837
+ stale: false,
89838
+ managed: false,
89839
+ current: r.currentVersion,
89840
+ latest: r.currentVersion,
89841
+ channel: r.channel
89842
+ };
89845
89843
  }
89846
89844
  return {
89847
- stale: latest !== undefined ? latest !== running : r.status === "available",
89845
+ stale: r.status === "available",
89848
89846
  managed: false,
89849
- current: running,
89850
- latest: latest ?? r.latestVersion,
89851
- channel: latest !== undefined ? "npm" : r.channel
89847
+ current: r.currentVersion,
89848
+ latest: r.latestVersion,
89849
+ channel: r.channel
89852
89850
  };
89853
89851
  } catch {
89852
+ const latest = await dependencies.fetchPackageVersion(opts).catch(() => {
89853
+ return;
89854
+ });
89854
89855
  return {
89855
89856
  stale: latest !== undefined && latest !== running,
89856
89857
  managed: true,
@@ -89859,8 +89860,9 @@ async function cliStale(opts) {
89859
89860
  };
89860
89861
  }
89861
89862
  }
89862
- async function fetchNpmLatestVersion() {
89863
- const response2 = await fetch("https://registry.npmjs.org/@astrale-os/cli/latest");
89863
+ async function fetchNpmTargetVersion(opts) {
89864
+ const target2 = opts.version ?? (opts.channel === "stable" ? "latest" : opts.channel) ?? "latest";
89865
+ const response2 = await fetch(`https://registry.npmjs.org/@astrale-os/cli/${target2}`);
89864
89866
  if (!response2.ok)
89865
89867
  throw new Error(`npm registry HTTP ${response2.status}`);
89866
89868
  const body = await response2.json();
@@ -89876,7 +89878,7 @@ async function sdkStale() {
89876
89878
  const outdated = await findSdkOutdated();
89877
89879
  return { stale: outdated.length > 0, inProject: true, outdated };
89878
89880
  }
89879
- var update_default;
89881
+ var CLI_STALE_DEPENDENCIES, update_default;
89880
89882
  var init_update2 = __esm(() => {
89881
89883
  init_package();
89882
89884
  init_log();
@@ -89885,6 +89887,10 @@ var init_update2 = __esm(() => {
89885
89887
  init_sdk_deps();
89886
89888
  init_skills();
89887
89889
  init_update();
89890
+ CLI_STALE_DEPENDENCIES = {
89891
+ update: updateAstrale,
89892
+ fetchPackageVersion: fetchNpmTargetVersion
89893
+ };
89888
89894
  update_default = {
89889
89895
  name: "update",
89890
89896
  description: "Check for and install Astrale CLI updates",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrale-os/cli",
3
- "version": "1.0.0-beta.0",
3
+ "version": "1.0.0-beta.1",
4
4
  "description": "Astrale CLI — connect to existing Astrale kernels",
5
5
  "keywords": [
6
6
  "astrale",
@@ -153,7 +153,7 @@ describe('V2 Admin Instance adapter', () => {
153
153
  )
154
154
  })
155
155
 
156
- test('invokes Fleet.createInstance with an internal operation identity', async () => {
156
+ test('delegates default placement to Fleet without reading Host inventory', async () => {
157
157
  const created = {
158
158
  id: '@instance-node',
159
159
  slug: 'demo',
@@ -179,6 +179,8 @@ describe('V2 Admin Instance adapter', () => {
179
179
  Path.id(NodeId('fleet')),
180
180
  { operationId: 'cli.instance.create:test', slug: 'demo' },
181
181
  )
182
+ expect(contract.graph.query).not.toHaveBeenCalled()
183
+ expect(contract.graph.neighbors).not.toHaveBeenCalled()
182
184
  })
183
185
 
184
186
  test('resolves --host-id to an exact Host receiver and rejects the reserved Admin Host', async () => {
@@ -119,15 +119,9 @@ export async function connectAdminInstances(
119
119
  )
120
120
  }
121
121
 
122
- // Preserve the existing interactive multi-Host picker without accepting a
123
- // doomed operation first. Graph read policy exposes only caller-usable
124
- // Hosts; the reserved Admin Host is excluded explicitly.
125
- const inventory = await hostInventory(context.graph, binding, Host, fleet)
126
- const eligible = inventory.hosts.filter(
127
- (host) => host.state === 'ready' && host.path !== inventory.reserved,
128
- )
129
- if (eligible.length > 1) throw hostSelectionRequired(eligible.map((host) => host.id))
130
-
122
+ // Default placement belongs to Admin. Avoid a redundant Host graph read:
123
+ // it requires broader graph authority than this Fleet operation and can
124
+ // deny an otherwise authorized caller before the server selects a Host.
131
125
  return instanceFromSummary(
132
126
  await binding.$.invoke(Fleet.$.method('createInstance') as never, fleet, input as never),
133
127
  )
@@ -200,13 +194,6 @@ async function hostInventory(
200
194
  })
201
195
  }
202
196
 
203
- function hostSelectionRequired(ids: readonly string[]): Error {
204
- return new Error(
205
- `alphaCreate could not choose a host: ${ids.length} ready hosts are assigned (${[...ids].sort().join(', ')}). ` +
206
- 'Specify host_id once multi-host placement is enabled.',
207
- )
208
- }
209
-
210
197
  type DynamicDefinition = ReturnType<DomainBinding['$']['class']>
211
198
 
212
199
  function instanceFromSummary(input: unknown): InstanceInfo {
@@ -0,0 +1,66 @@
1
+ import { describe, expect, mock, test } from 'bun:test'
2
+
3
+ import { cliStale, fetchNpmTargetVersion } from '../update'
4
+
5
+ describe('CLI update staleness', () => {
6
+ test('trusts the release manifest for a script install without consulting npm latest', async () => {
7
+ const fetchPackageVersion = mock(async () => '0.8.1-alpha.7')
8
+ const result = await cliStale(
9
+ { channel: 'beta' },
10
+ {
11
+ update: async () => ({
12
+ status: 'up-to-date',
13
+ currentVersion: '1.0.0-beta.0',
14
+ latestVersion: '1.0.0-beta.0',
15
+ channel: 'beta',
16
+ }),
17
+ fetchPackageVersion,
18
+ },
19
+ )
20
+
21
+ expect(result).toEqual({
22
+ stale: false,
23
+ managed: false,
24
+ current: '1.0.0-beta.0',
25
+ latest: '1.0.0-beta.0',
26
+ channel: 'beta',
27
+ })
28
+ expect(fetchPackageVersion).not.toHaveBeenCalled()
29
+ })
30
+
31
+ test('uses the selected npm dist-tag only for package-managed installs', async () => {
32
+ const result = await cliStale(
33
+ { channel: 'beta' },
34
+ {
35
+ update: async () => {
36
+ throw new Error('package managed')
37
+ },
38
+ fetchPackageVersion: async ({ channel }) => {
39
+ expect(channel).toBe('beta')
40
+ return '1.0.0-beta.0'
41
+ },
42
+ },
43
+ )
44
+
45
+ expect(result).toMatchObject({
46
+ managed: true,
47
+ latest: '1.0.0-beta.0',
48
+ channel: 'npm',
49
+ })
50
+ })
51
+
52
+ test('maps the stable channel to the npm latest dist-tag', async () => {
53
+ const originalFetch = globalThis.fetch
54
+ const fetchMock = mock(async (input: RequestInfo | URL) => {
55
+ expect(String(input)).toBe('https://registry.npmjs.org/@astrale-os/cli/latest')
56
+ return Response.json({ version: '1.0.0' })
57
+ })
58
+ globalThis.fetch = fetchMock as unknown as typeof fetch
59
+ try {
60
+ expect(await fetchNpmTargetVersion({ channel: 'stable' })).toBe('1.0.0')
61
+ expect(fetchMock).toHaveBeenCalledTimes(1)
62
+ } finally {
63
+ globalThis.fetch = originalFetch
64
+ }
65
+ })
66
+ })
@@ -115,27 +115,46 @@ export type StaleReport = {
115
115
  sdk: { stale: boolean; inProject: boolean; outdated: SdkOutdated[] }
116
116
  }
117
117
 
118
- async function cliStale(opts: UpdateOpts): Promise<StaleReport['cli']> {
118
+ type CliStaleDependencies = {
119
+ update: typeof updateAstrale
120
+ fetchPackageVersion: (opts: Pick<UpdateOpts, 'channel' | 'version'>) => Promise<string>
121
+ }
122
+
123
+ const CLI_STALE_DEPENDENCIES: CliStaleDependencies = {
124
+ update: updateAstrale,
125
+ fetchPackageVersion: fetchNpmTargetVersion,
126
+ }
127
+
128
+ export async function cliStale(
129
+ opts: Pick<UpdateOpts, 'channel' | 'version'>,
130
+ dependencies: CliStaleDependencies = CLI_STALE_DEPENDENCIES,
131
+ ): Promise<StaleReport['cli']> {
119
132
  const running = pkg.version
120
- const latest = await fetchNpmLatestVersion().catch(() => undefined)
121
133
  try {
122
- const r = await updateAstrale({
134
+ const r = await dependencies.update({
123
135
  check: true,
124
136
  channel: opts.channel,
125
137
  version: opts.version,
126
138
  currentVersion: running,
127
139
  })
128
140
  if (r.status === 'updated') {
129
- return { stale: false, managed: false, current: running, latest: latest ?? running }
141
+ return {
142
+ stale: false,
143
+ managed: false,
144
+ current: r.currentVersion,
145
+ latest: r.currentVersion,
146
+ channel: r.channel,
147
+ }
130
148
  }
131
149
  return {
132
- stale: latest !== undefined ? latest !== running : r.status === 'available',
150
+ stale: r.status === 'available',
133
151
  managed: false,
134
- current: running,
135
- latest: latest ?? r.latestVersion,
136
- channel: latest !== undefined ? 'npm' : r.channel,
152
+ current: r.currentVersion,
153
+ latest: r.latestVersion,
154
+ channel: r.channel,
137
155
  }
138
156
  } catch {
157
+ const latest = await dependencies.fetchPackageVersion(opts).catch(() => undefined)
139
158
  return {
140
159
  stale: latest !== undefined && latest !== running,
141
160
  managed: true,
@@ -145,8 +164,11 @@ async function cliStale(opts: UpdateOpts): Promise<StaleReport['cli']> {
145
164
  }
146
165
  }
147
166
 
148
- async function fetchNpmLatestVersion(): Promise<string> {
149
- const response = await fetch('https://registry.npmjs.org/@astrale-os/cli/latest')
167
+ export async function fetchNpmTargetVersion(
168
+ opts: Pick<UpdateOpts, 'channel' | 'version'>,
169
+ ): Promise<string> {
170
+ const target = opts.version ?? (opts.channel === 'stable' ? 'latest' : opts.channel) ?? 'latest'
171
+ const response = await fetch(`https://registry.npmjs.org/@astrale-os/cli/${target}`)
150
172
  if (!response.ok) throw new Error(`npm registry HTTP ${response.status}`)
151
173
  const body: unknown = await response.json()
152
174
  if (