@phreshos/server 0.1.21 → 0.1.23

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
@@ -136,6 +136,9 @@ A Program owns its scoped Process capability:
136
136
  const processes = await program.process.list()
137
137
  const process = await program.process.find("worker")
138
138
  const created = await program.process.create({ name: "worker" })
139
+ for await (const event of program.process.run({ name: "temporary" }, { signal })) {
140
+ // started, output, exited
141
+ }
139
142
  const shared = await program.process.findOrCreate({
140
143
  name: "shared-server",
141
144
  server: true,
@@ -143,6 +146,11 @@ const shared = await program.process.findOrCreate({
143
146
  })
144
147
  ```
145
148
 
149
+ `run()` creates exactly one Process and yields its ordered `started`, `output`,
150
+ and `exited` lifecycle. Aborting its signal, returning early, or losing the
151
+ calling Server exits that Process; `create()` remains independent of the
152
+ caller.
153
+
146
154
  `findOrCreate()` is atomic at the authoritative Core. Concurrent equivalent
147
155
  launches converge on one named Process; a different launch for that name
148
156
  rejects instead of reconfiguring the existing Process.
@@ -172,7 +180,7 @@ if (program.hasAgent) console.log(await program.agent())
172
180
  asset-hosting address. Omitting the size selects `medium`.
173
181
 
174
182
  Installed Programs may persist one ordinary Process launch for the next system
175
- startup. This capability exists only in the Server SDK:
183
+ startup through the shared Program interface:
176
184
 
177
185
  ```ts
178
186
  await program.startup.enable({
package/dist/domain.d.ts CHANGED
@@ -74,11 +74,26 @@ export interface ProgramProcess extends Omit<CoreProgramProcess, "list" | "first
74
74
  find(identityOrName: string): Promise<Process | null>;
75
75
  /** Creates one Process of this Program. */
76
76
  create(launch?: Launch): Promise<Process>;
77
+ /** Runs one Process for exactly as long as its lifecycle iterator remains open. */
78
+ run(launch?: Launch, options?: ProgramProcessRunOptions): AsyncGenerator<ProgramProcessRunEvent, void, void>;
77
79
  /** Finds the named Process or atomically creates it with the same resolved launch. */
78
80
  findOrCreate(launch: Launch & Readonly<{
79
81
  name: string;
80
82
  }>): Promise<Process>;
81
83
  }
84
+ export type ProgramProcessRunEvent = Readonly<{
85
+ event: "started";
86
+ process: Process;
87
+ }> | (Readonly<{
88
+ event: "output";
89
+ }> & ProgramCommandChunk) | Readonly<{
90
+ event: "exited";
91
+ process: Process;
92
+ exit: Exit;
93
+ }>;
94
+ export type ProgramProcessRunOptions = Readonly<{
95
+ signal?: AbortSignal;
96
+ }>;
82
97
  /** Server-visible Process handle. */
83
98
  export interface Process<Events extends object = {}> extends CoreProcess<Events> {
84
99
  /** Permanent handle to this Process's Server. */
package/dist/domain.js CHANGED
@@ -134,6 +134,11 @@ class ProgramProcessHandle {
134
134
  const answer = await wire.request(["program-process-create", this.address, launch]);
135
135
  return process(answer[0]);
136
136
  }
137
+ async *run(launch = {}, options = {}) {
138
+ for await (const value of wire.stream(["run", this.address, launch], undefined, options.signal)) {
139
+ yield processRunEvent(value);
140
+ }
141
+ }
137
142
  async findOrCreate(launch) {
138
143
  const answer = await wire.request(["program-process-find-or-create", this.address, launch]);
139
144
  return process(answer[0]);
@@ -143,6 +148,21 @@ class ProgramProcessHandle {
143
148
  return answer[0];
144
149
  }
145
150
  }
151
+ function processRunEvent(value) {
152
+ const event = value;
153
+ if (event?.event === "started" && event.process) {
154
+ return Object.freeze({ event: "started", process: process(event.process) });
155
+ }
156
+ if (event?.event === "output" && (event.stream === "stdout" || event.stream === "stderr") && typeof event.text === "string") {
157
+ return Object.freeze({ event: "output", stream: event.stream, text: event.text });
158
+ }
159
+ if (event?.event === "exited" && event.process) {
160
+ const code = typeof event.exit?.code === "number" ? event.exit.code : null;
161
+ const signal = typeof event.exit?.signal === "string" ? event.exit.signal : null;
162
+ return Object.freeze({ event: "exited", process: process(event.process), exit: exit(code, signal) });
163
+ }
164
+ throw new Error("The System returned an invalid Process run event");
165
+ }
146
166
  /** Internal transport address for a Program handle created by this SDK. */
147
167
  export function programAddress(value) {
148
168
  if (!(value instanceof ProgramHandle))
package/dist/main.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- export { system, type System, type SystemProcess, type SystemProgram, type ClientDescription, type SystemProcessEvents, type SystemProcessExit, type SystemProgramEvents, type SystemProgramUninstall, type ProgramDescription, type ServerDescription } from "./system.js";
1
+ export { system } from "./system.js";
2
2
  export { current, type Current, type CurrentClient } from "./current.js";
3
3
  export { type Answerer, type Channel, type ChannelCapture, type ChannelEvents, type ChannelMessage } from "./channel.js";
4
4
  export { type ProgramStartup } from "./startup.js";
5
5
  export { type Storage } from "./storage.js";
6
6
  export { ClientServiceHandler, ServerServiceHandler } from "./service.js";
7
7
  export { ServiceHandler, type ClientServiceChannel, type ServerServiceChannel, type ServiceChannel, type ServiceKey, type ServiceLifecycleEvents } from "@phreshos/core";
8
- export { Client, Endpoint, Process, Program, Server, type Window, type ProgramProcess } from "./domain.js";
9
- export type { AnswerCapture, AnswerMessage, AnswerObserver, Askable, AskCapture, AskMessage, AskObserver, Capture, Captures, ClientTraffic, ClientDeclaration, Cleanup, DirectoryStat, EndpointDeclaration, EndpointTraffic, EntryStat, EventMessage, EventName, EventObserver, EventOptions, EventSubscriber, Exit, FileStat, Launch, LaunchClient, Layer, LogKind, LogRecord, LogSource, Message, OtherStat, Outcome, Position, ProgramEvents, ProgramPermission, ProgramProcessEvents, ProgramProcessExit, ProgramSql, ProgramStore, ProcessEvents, Publishable, SystemUploads, Upload, ServerTraffic, Size, Subscribable, SubscribableEvents, SubscribableFallback, TimedAskable, TrafficCapture, TrafficEvents, TrafficMessage, Value, Appearance, AppearanceEvents, AppearanceSource, AppearanceSurface, ThemedValue, WritableAppearance, WindowEvents, WindowGeometry, WindowLayer, WindowState } from "@phreshos/core";
8
+ export { Client, Endpoint, Process, Program, Server, type Window, type ProgramProcess, type ProgramProcessRunEvent, type ProgramProcessRunOptions } from "./domain.js";
9
+ export type { AnswerCapture, AnswerMessage, AnswerObserver, Askable, AskCapture, AskMessage, AskObserver, Capture, Captures, ClientTraffic, ClientDeclaration, Cleanup, DirectoryStat, EndpointDeclaration, EndpointTraffic, EntryStat, EventMessage, EventName, EventObserver, EventOptions, EventSubscriber, Exit, FileStat, Launch, LaunchClient, Layer, LogKind, LogRecord, LogSource, Message, OtherStat, Outcome, Position, ProgramEvents, ProgramPermission, ProgramProcessEvents, ProgramProcessExit, ProgramSql, ProgramStore, ProcessEvents, Publishable, SystemUploads, Upload, ServerTraffic, System, SystemProcess, SystemProcessEntity, SystemProcessEntityEvents, SystemProgram, SystemProgramEntity, SystemProgramProcess, SystemProgramProcessEvents, SystemEndpointEntity, SystemServerEntity, SystemClientEntity, ClientDescription, SystemProcessEvents, SystemProcessExit, SystemProgramEvents, SystemProgramUninstall, ProgramDescription, ServerDescription, Size, Subscribable, SubscribableEvents, SubscribableFallback, TimedAskable, TrafficCapture, TrafficEvents, TrafficMessage, Value, Appearance, AppearanceEvents, AppearanceSource, AppearanceSurface, ThemedValue, WritableAppearance, WindowEvents, WindowGeometry, WindowLayer, WindowState } from "@phreshos/core";
package/dist/system.d.ts CHANGED
@@ -1,133 +1,3 @@
1
- import type { Exit, Layer, Position, ServiceKey, Size, Subscribable, SystemUploads, WritableAppearance } from "@phreshos/core";
2
- import type { ClientServiceHandler, ServerServiceHandler } from "./service.js";
3
- import { type Client, type Process, type Program, type Server } from "./domain.js";
4
- import { type Storage } from "./storage.js";
5
- /** Resolved production description for a Program's Server. */
6
- type ServerDescriptionBase = Readonly<{
7
- /** Absolute directory containing the production Server files. */
8
- location: string;
9
- /** Whether newly created Processes start this Server by default. */
10
- start?: boolean;
11
- /** Command used to install the Server's production dependencies. */
12
- installCommand?: string;
13
- }>;
14
- export type ServerDescription = ServerDescriptionBase & (Readonly<{
15
- /** Command used to start an isolated Server process. */
16
- startCommand: string;
17
- entryFile?: never;
18
- }> | Readonly<{
19
- startCommand?: never;
20
- /** JavaScript module loaded as a worker owned by the System. */
21
- entryFile: string;
22
- }>);
23
- /** Resolved production description for a Program's Client and initial Window. */
24
- export type ClientDescription = Readonly<{
25
- /** Absolute directory containing the production Client files. */
26
- location: string;
27
- /** Whether newly created Processes start this Client by default. */
28
- start?: boolean;
29
- /** Default Window title. */
30
- title?: string;
31
- /** Default Window size. */
32
- size?: Size;
33
- /** Default Window position. */
34
- position?: Position;
35
- /** Default Window layer. */
36
- layer?: Layer;
37
- /** Whether the Window starts minimized. */
38
- minimize?: boolean;
39
- }>;
40
- type Description = Readonly<{
41
- /** Stable identity assigned to the Program. */
42
- identity: string;
43
- /** Human-readable Program name. */
44
- name?: string;
45
- /** Declared Program version. */
46
- version?: string;
47
- /** Short human-readable Program description. */
48
- description?: string;
49
- /** Absolute validated PNG source used to derive the Program's hosted icon sizes. */
50
- icon?: string;
51
- /** Absolute Markdown file describing Program-specific operation to agents. */
52
- agent?: string;
53
- /** Absolute directory used for the Program's persistent storage. */
54
- storage: string;
55
- }>;
56
- /** Complete runtime description used to create a Program. */
57
- export type ProgramDescription = Description & (Readonly<{
58
- /** Required Server description when no Client is described. */
59
- server: ServerDescription;
60
- /** Optional Client description. */
61
- client?: ClientDescription;
62
- }> | Readonly<{
63
- /** Optional Server description. */
64
- server?: ServerDescription;
65
- /** Required Client description when no Server is described. */
66
- client: ClientDescription;
67
- }>);
68
- /** An uninstall reported with the affected Program and removal scope. */
69
- export type SystemProgramUninstall = Readonly<{
70
- /** Program that left the installed state. */
71
- program: Program;
72
- /** Whether all installed resources, including storage, were removed. */
73
- everythingRemoved: boolean;
74
- }>;
75
- /** A Process exit reported with the Process that ended. */
76
- export type SystemProcessExit = Exit & Readonly<{
77
- /** Process that ended. */
78
- process: Process;
79
- }>;
80
- /** Authoritative lifecycle events visible to the Server system. */
81
- export type SystemProgramEvents = {
82
- /** A Program entered the runtime registry. */
83
- create: Program;
84
- /** A Program left the runtime registry. */
85
- forget: Program;
86
- /** A Program entered the installed state. */
87
- install: Program;
88
- /** A Program left the installed state. */
89
- uninstall: SystemProgramUninstall;
90
- };
91
- /** Authoritative Process lifecycle events visible to the Server system. */
92
- export type SystemProcessEvents = {
93
- /** One Process Endpoint entered a new live incarnation. */
94
- endpointStart: Server | Client;
95
- /** One Process Endpoint incarnation ended. */
96
- endpointStop: Server | Client;
97
- /** A Process entered the runtime set. */
98
- create: Process;
99
- /** A Process left the runtime set. */
100
- exit: SystemProcessExit;
101
- };
102
- /** Authoritative Program registry available to a Server endpoint. */
103
- export interface SystemProgram extends Subscribable<SystemProgramEvents, never> {
104
- list(onlyInstalled?: boolean): Promise<Program[]>;
105
- find(identity: string): Promise<Program | null>;
106
- create(source: ProgramDescription | string): Promise<Program>;
107
- }
108
- /** Authoritative Process registry available to a Server endpoint. */
109
- export interface SystemProcess extends Subscribable<SystemProcessEvents, never> {
110
- list(): Promise<Process[]>;
111
- find(identity: string): Promise<Process | null>;
112
- }
113
- /** Authoritative system capabilities available to a Server endpoint. */
114
- export interface System {
115
- /** Native operating-system home storage available to Server endpoints. */
116
- readonly storage: Storage;
117
- /** Complete unresolved Appearance authority owned by the System. */
118
- readonly appearance: WritableAppearance;
119
- readonly program: SystemProgram;
120
- readonly process: SystemProcess;
121
- /** Flat System-owned public uploads capability. */
122
- readonly uploads: SystemUploads;
123
- /** Returns a stable handle for one exact Service identity. */
124
- service<ServiceEvents extends object = {}>(key: ServiceKey & {
125
- endpoint: "server";
126
- }): ServerServiceHandler<ServiceEvents>;
127
- service<ServiceEvents extends object = {}>(key: ServiceKey & {
128
- endpoint: "client";
129
- }): ClientServiceHandler<ServiceEvents>;
130
- }
1
+ import type { System } from "@phreshos/core";
131
2
  /** Authoritative system capabilities for the currently executing Server. */
132
3
  export declare const system: System;
133
- export {};
package/dist/system.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import Events from "./events.js";
2
- import { exit, lifecycleEndpoint, process, program } from "./domain.js";
2
+ import { exit, lifecycleEndpoint, process, program, } from "./domain.js";
3
3
  import { uploads } from "./uploads.js";
4
4
  import ServerAppearance from "./appearance.js";
5
5
  import wire from "./wire.js";
@@ -11,6 +11,10 @@ class ServerSystem {
11
11
  program = new ServerSystemProgram();
12
12
  process = new ServerSystemProcess();
13
13
  uploads = uploads;
14
+ async forceCreateProgram(source) {
15
+ const answer = await wire.request(["host-program-force-create", source]);
16
+ return program(answer[0]);
17
+ }
14
18
  service(key) { return prepareService(key); }
15
19
  }
16
20
  class ServerSystemProgram extends Events {
package/dist/wire.d.ts CHANGED
@@ -20,7 +20,7 @@ declare class Wire {
20
20
  request(values: unknown[], timeout?: number): Promise<unknown>;
21
21
  requestWithin(values: unknown[], deadline: Deadline): Promise<unknown>;
22
22
  /** Opens one long-running system operation and yields its ordered values. */
23
- stream(values: unknown[], timeout?: number): AsyncIterableIterator<unknown>;
23
+ stream(values: unknown[], timeout?: number, signal?: AbortSignal): AsyncIterableIterator<unknown>;
24
24
  /** Resolves this endpoint's Process address only for operations that need it. */
25
25
  identity(): Promise<{
26
26
  process: string;
package/dist/wire.js CHANGED
@@ -74,7 +74,7 @@ class Wire {
74
74
  });
75
75
  }
76
76
  /** Opens one long-running system operation and yields its ordered values. */
77
- stream(values, timeout = defaultTimeout) {
77
+ stream(values, timeout = defaultTimeout, signal) {
78
78
  const wire = this;
79
79
  return (async function* () {
80
80
  const question = randomUUID();
@@ -90,6 +90,15 @@ class Wire {
90
90
  state.wake = null;
91
91
  }, timeout)
92
92
  };
93
+ const abort = () => {
94
+ state.failure = signal?.reason instanceof Error ? signal.reason : new Error("The operation was cancelled");
95
+ state.wake?.();
96
+ state.wake = null;
97
+ };
98
+ if (signal?.aborted)
99
+ abort();
100
+ else
101
+ signal?.addEventListener("abort", abort, { once: true });
93
102
  wire.send("boundary", "expect", question);
94
103
  wire.streams.set(question, state);
95
104
  wire.send("end-host", "stream", question, ...values);
@@ -107,6 +116,7 @@ class Wire {
107
116
  }
108
117
  }
109
118
  finally {
119
+ signal?.removeEventListener("abort", abort);
110
120
  clearTimeout(state.timer);
111
121
  wire.streams.delete(question);
112
122
  wire.send("boundary", "forget", question);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phreshos/server",
3
- "version": "0.1.21",
3
+ "version": "0.1.23",
4
4
  "description": "The SDK used by a Program's server endpoint.",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",
@@ -47,13 +47,13 @@
47
47
  "prepack": "node --run build"
48
48
  },
49
49
  "peerDependencies": {
50
- "@phreshos/core": "^0.1.20"
50
+ "@phreshos/core": "^0.1.22"
51
51
  },
52
52
  "dependencies": {
53
53
  "@msgpack/msgpack": "^3.1.3"
54
54
  },
55
55
  "devDependencies": {
56
- "@phreshos/core": "^0.1.20",
56
+ "@phreshos/core": "^0.1.22",
57
57
  "@types/node": "^26.2.0",
58
58
  "typescript": "^6.0.3"
59
59
  }