@agentshouse/app-bridge 0.1.0-alpha.6 → 0.1.0-alpha.7

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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  The renderer-side client of the agents.house App bridge. An App served by House
4
4
  receives one message port before its own scripts run; this package is that
5
- port's one listener. It carries four families:
5
+ port's one listener. It carries five families:
6
6
 
7
7
  - **Navigation** - the part of the shell's URL below the App's path, with its
8
8
  query and fragment. The shell delivers it when the App starts and whenever the
@@ -29,6 +29,16 @@ port's one listener. It carries four families:
29
29
  - **Site image read** - given a Published Room and an original attachment
30
30
  reference, it answers the web copy's path and metadata, or `null` when no
31
31
  copy exists. The original bytes never cross this bridge at a Site.
32
+ - **App release code** - `sourceTop(app, release)` answers the top level of
33
+ the latest successful release of an App the User may use: its folders, its
34
+ files with their sizes and its root README's text, or `null` for none;
35
+ `sourceDownload(app, release)` answers every file of that release with its
36
+ path, its size and its `text`, or its bytes as `base64`. The browser keeps
37
+ each answer per App and release: a read naming a release it holds answers
38
+ without asking House, and keeping a newer release drops the earlier one. A
39
+ refusal is House's `{ error: { code } }`, `app_release_not_found` for an
40
+ App with no release; a read House does not answer, or one that outlived
41
+ the browser's signed-in User, is `null`.
32
42
 
33
43
  The package carries no credential handling and no Runtime family. It surfaces
34
44
  the dedicated Runtime port the worker transfers over the bridge as a plain
@@ -88,4 +98,5 @@ same-origin absolute path, publishes a status, its `service_mode` included,
88
98
  records in `consents` each
89
99
  `gaConsent` it takes while that status measures and publishes the status the
90
100
  choice leaves, records in `tabs` each `setTab` the shell would take and ignores
91
- any other, and attaches a Runtime port.
101
+ any other, answers a release code read from the `sources` it was given, or
102
+ `null`, and attaches a Runtime port.
@@ -1,4 +1,4 @@
1
- import type { HouseBridgePort, HouseGaChoice, HouseMessagePort, HouseStatus, HouseTab } from './wire.js';
1
+ import type { HouseBridgePort, HouseGaChoice, HouseMessagePort, HouseSourceDownload, HouseSourceRefusal, HouseSourceTop, HouseStatus, HouseTab } from './wire.js';
2
2
  export declare class HouseCeremonyRefused extends Error {
3
3
  readonly path: string | null;
4
4
  constructor(path: string | null);
@@ -22,6 +22,8 @@ export interface HouseBridge {
22
22
  ceremony(path: string): Promise<never>;
23
23
  setTab(tab: HouseTab): void;
24
24
  readAttachment(room: string, attachment: string): Promise<HouseImageCopy | null>;
25
+ sourceTop(app: string, release?: string): Promise<HouseSourceTop | HouseSourceRefusal | null>;
26
+ sourceDownload(app: string, release?: string): Promise<HouseSourceDownload | HouseSourceRefusal | null>;
25
27
  status(): HouseStatus | null;
26
28
  onStatus(listener: (status: HouseStatus) => void): () => void;
27
29
  gaConsent(choice: HouseGaChoice): void;
@@ -33,6 +33,8 @@ export function connectHouseBridge(source = heldBridge()) {
33
33
  const navigations = [];
34
34
  const attachments = new Map();
35
35
  let nextAttachment = 0;
36
+ const sources = new Map();
37
+ let nextSource = 0;
36
38
  const addressListeners = new Set();
37
39
  const statusListeners = new Set();
38
40
  let outbox = [];
@@ -88,6 +90,17 @@ export function connectHouseBridge(source = heldBridge()) {
88
90
  pending.resolve(answer.image ?? null);
89
91
  return;
90
92
  }
93
+ if (data.house === 'source') {
94
+ const answer = data;
95
+ if (typeof answer.id !== 'number')
96
+ return;
97
+ const pending = sources.get(answer.id);
98
+ if (pending === undefined)
99
+ return;
100
+ sources.delete(answer.id);
101
+ pending.resolve(answer.answer ?? null);
102
+ return;
103
+ }
91
104
  if (data.house === 'ceremony-refused') {
92
105
  const refused = data.path;
93
106
  const named = typeof refused === 'string' ? refused : null;
@@ -105,6 +118,13 @@ export function connectHouseBridge(source = heldBridge()) {
105
118
  for (const message of waiting)
106
119
  port.postMessage(message);
107
120
  });
121
+ function readSource(house, app, release) {
122
+ const pending = settled();
123
+ const id = ++nextSource;
124
+ sources.set(id, pending.settle);
125
+ send({ house, id, app, release });
126
+ return pending.promise;
127
+ }
108
128
  function navigate(mode, asked) {
109
129
  const pending = settled();
110
130
  navigations.push({ address: asked, settle: pending.settle });
@@ -144,6 +164,12 @@ export function connectHouseBridge(source = heldBridge()) {
144
164
  send({ house: 'attachment-read', id, room, attachment });
145
165
  return pending.promise;
146
166
  },
167
+ sourceTop(app, release) {
168
+ return readSource('source-top', app, release);
169
+ },
170
+ sourceDownload(app, release) {
171
+ return readSource('source-download', app, release);
172
+ },
147
173
  status() {
148
174
  return status;
149
175
  },
@@ -1,3 +1,3 @@
1
1
  export { connectHouseBridge, HouseCeremonyRefused, HouseNavigationRefused, type HouseBridge, type HouseImageCopy, } from './bridge.js';
2
2
  export { HOUSE_ADDRESS_MAX_CHARS } from './wire.js';
3
- export type { HouseBridgeEvent, HouseBridgePort, HouseCredentialState, HouseGa, HouseGaChoice, HouseMessagePort, HouseNavigationMode, HouseStatus, HouseSyncState, } from './wire.js';
3
+ export type { HouseBridgeEvent, HouseBridgePort, HouseCredentialState, HouseGa, HouseGaChoice, HouseMessagePort, HouseNavigationMode, HouseSourceDownload, HouseSourceFile, HouseSourceRefusal, HouseSourceTop, HouseStatus, HouseSyncState, } from './wire.js';
@@ -1,5 +1,5 @@
1
1
  import type { HouseImageCopy } from './bridge.js';
2
- import type { HouseBridgePort, HouseGaChoice, HouseMessagePort, HouseNavigationMode, HouseStatus, HouseTab } from './wire.js';
2
+ import type { HouseBridgePort, HouseGaChoice, HouseMessagePort, HouseNavigationMode, HouseSourceDownload, HouseSourceTop, HouseStatus, HouseTab } from './wire.js';
3
3
  export interface HouseBridgeNavigation {
4
4
  readonly mode: HouseNavigationMode;
5
5
  readonly address: string;
@@ -21,5 +21,9 @@ export interface HouseBridgeTestHostOptions {
21
21
  readonly images?: readonly (HouseImageCopy & {
22
22
  readonly room: string;
23
23
  })[];
24
+ readonly sources?: readonly {
25
+ readonly top: HouseSourceTop;
26
+ readonly download: HouseSourceDownload;
27
+ }[];
24
28
  }
25
29
  export declare function createHouseBridgeTestHost(options?: HouseBridgeTestHostOptions): HouseBridgeTestHost;
@@ -87,6 +87,12 @@ export function createHouseBridgeTestHost(options = {}) {
87
87
  else if (data.house === 'ga/consent') {
88
88
  consent(data.choice);
89
89
  }
90
+ else if (data.house === 'source-top' || data.house === 'source-download') {
91
+ const asked = data;
92
+ const source = options.sources?.find((entry) => entry.top.app_ref === asked.app);
93
+ const answer = source === undefined ? null : data.house === 'source-top' ? source.top : source.download;
94
+ deliver({ house: 'source', id: asked.id, answer });
95
+ }
90
96
  else if (data.house === 'attachment-read') {
91
97
  const asked = data;
92
98
  const image = options.images?.find((entry) => entry.room === asked.room && entry.attachment === asked.attachment);
@@ -36,5 +36,34 @@ export interface HouseStatus {
36
36
  readonly service_mode: 'open' | 'write-halted' | null;
37
37
  }
38
38
  export type HouseNavigationMode = 'push' | 'replace';
39
+ interface HouseRelease {
40
+ readonly app_ref: string;
41
+ readonly release: string;
42
+ }
43
+ export interface HouseSourceTop extends HouseRelease {
44
+ readonly folders: readonly string[];
45
+ readonly files: readonly {
46
+ readonly name: string;
47
+ readonly size: number;
48
+ }[];
49
+ readonly readme: string | null;
50
+ }
51
+ export type HouseSourceFile = {
52
+ readonly path: string;
53
+ readonly size: number;
54
+ } & ({
55
+ readonly text: string;
56
+ } | {
57
+ readonly base64: string;
58
+ });
59
+ export interface HouseSourceDownload extends HouseRelease {
60
+ readonly files: readonly HouseSourceFile[];
61
+ }
62
+ export interface HouseSourceRefusal {
63
+ readonly error: {
64
+ readonly code: string;
65
+ };
66
+ }
39
67
  export type HouseTab = TabChange;
40
68
  export declare const HOUSE_ADDRESS_MAX_CHARS = 1000;
69
+ export {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agentshouse/app-bridge",
3
- "version": "0.1.0-alpha.6",
4
- "description": "Renderer-side client of the agents.house App bridge: navigation, the tab title and icon, ceremonies, status and Site image reads, with an in-page test host.",
3
+ "version": "0.1.0-alpha.7",
4
+ "description": "Renderer-side client of the agents.house App bridge: navigation, the tab title and icon, ceremonies, status, Site image reads and App release code, with an in-page test host.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
7
7
  "sideEffects": false,