@nylorun/runtime 0.8.0-beta → 0.10.0-beta
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/CHANGELOG.md +52 -0
- package/README.md +96 -25
- package/dist/adapters/media.d.ts +2 -2
- package/dist/adapters/media.js +2 -2
- package/dist/adapters/observe.d.ts +2 -1
- package/dist/adapters/observe.js +2 -3
- package/dist/assets.d.ts +2 -2
- package/dist/assets.js +5 -3
- package/dist/core/executors.d.ts +11 -2
- package/dist/core/executors.js +20 -4
- package/dist/core/flow-host.d.ts +264 -0
- package/dist/core/flow-host.js +783 -0
- package/dist/core/limits.d.ts +40 -0
- package/dist/core/limits.js +76 -0
- package/dist/core/runtime.d.ts +5 -90
- package/dist/core/runtime.js +3 -1418
- package/dist/core/sandbox-routes.d.ts +35 -0
- package/dist/core/sandbox-routes.js +106 -0
- package/dist/core/store.d.ts +5 -2
- package/dist/core/store.js +20 -72
- package/dist/core/turn-manifest.d.ts +50 -0
- package/dist/core/turn-manifest.js +66 -0
- package/dist/host/config-for.d.ts +16 -0
- package/dist/host/config-for.js +45 -0
- package/dist/host/config.d.ts +24 -0
- package/dist/host/create-host.d.ts +21 -0
- package/dist/host/create-host.js +396 -0
- package/dist/host/environment.d.ts +13 -0
- package/dist/host/environment.js +53 -0
- package/dist/host/http.d.ts +35 -0
- package/dist/host/http.js +136 -0
- package/dist/host/index.d.ts +6 -0
- package/dist/host/index.js +5 -0
- package/dist/host/logger.d.ts +3 -0
- package/dist/host/logger.js +18 -0
- package/dist/host/main.d.ts +3 -0
- package/dist/host/main.js +142 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.js +4 -2
- package/dist/launcher/commands.d.ts +24 -0
- package/dist/launcher/commands.js +260 -0
- package/dist/launcher/ensure-config.d.ts +16 -0
- package/dist/launcher/ensure-config.js +79 -0
- package/dist/launcher/errors.d.ts +12 -0
- package/dist/launcher/errors.js +22 -0
- package/dist/launcher/host-config.d.ts +17 -0
- package/dist/launcher/host-config.js +64 -0
- package/dist/launcher/host-state.d.ts +17 -0
- package/dist/launcher/host-state.js +61 -0
- package/dist/launcher/lifecycle.d.ts +66 -0
- package/dist/launcher/lifecycle.js +407 -0
- package/dist/launcher/locks.d.ts +26 -0
- package/dist/launcher/locks.js +117 -0
- package/dist/launcher/logs.d.ts +18 -0
- package/dist/launcher/logs.js +117 -0
- package/dist/launcher/main.d.ts +2 -0
- package/dist/launcher/main.js +66 -0
- package/dist/launcher/output.d.ts +11 -0
- package/dist/launcher/output.js +68 -0
- package/dist/launcher/paths.d.ts +20 -0
- package/dist/launcher/paths.js +36 -0
- package/dist/launcher/probe.d.ts +23 -0
- package/dist/launcher/probe.js +72 -0
- package/dist/launcher/protocol.d.ts +62 -0
- package/dist/launcher/protocol.js +1 -0
- package/dist/launcher/schema-guard.d.ts +6 -0
- package/dist/launcher/schema-guard.js +36 -0
- package/dist/launcher/spawn.d.ts +22 -0
- package/dist/launcher/spawn.js +85 -0
- package/dist/launcher/status.d.ts +6 -0
- package/dist/launcher/status.js +65 -0
- package/dist/mcp/connect.d.ts +1 -0
- package/dist/mcp/connect.js +2 -0
- package/dist/mcp/pool.d.ts +16 -2
- package/dist/mcp/pool.js +54 -19
- package/dist/mcp/snapshot.d.ts +4 -0
- package/dist/mcp/stdio.d.ts +2 -0
- package/dist/mcp/stdio.js +3 -1
- package/dist/model/auth-store.d.ts +1 -1
- package/dist/model/auth-store.js +2 -2
- package/dist/model/models.d.ts +4 -0
- package/dist/model/models.js +22 -26
- package/dist/model/pi-model.js +1 -1
- package/dist/model/settings.d.ts +5 -1
- package/dist/model/settings.js +6 -2
- package/dist/sandbox/select.d.ts +4 -3
- package/dist/sandbox/select.js +15 -9
- package/dist/sandbox/share.d.ts +23 -0
- package/dist/sandbox/share.js +58 -0
- package/dist/tenant/envelope.d.ts +10 -0
- package/dist/tenant/envelope.js +54 -0
- package/dist/tenant/ephemeral.d.ts +31 -0
- package/dist/tenant/ephemeral.js +156 -0
- package/dist/tenant/logger.d.ts +9 -0
- package/dist/tenant/logger.js +25 -0
- package/dist/tenant/migration.d.ts +16 -0
- package/dist/tenant/migration.js +116 -0
- package/dist/tenant/module.d.ts +17 -0
- package/dist/tenant/module.js +315 -0
- package/dist/tenant/paths.d.ts +24 -0
- package/dist/tenant/paths.js +65 -0
- package/dist/tenant/pool.d.ts +10 -0
- package/dist/tenant/pool.js +54 -0
- package/dist/tenant/principals.d.ts +17 -0
- package/dist/tenant/principals.js +44 -0
- package/dist/tenant/quarantine-error.d.ts +6 -0
- package/dist/tenant/quarantine-error.js +15 -0
- package/dist/tenant/quarantine.d.ts +35 -0
- package/dist/tenant/quarantine.js +94 -0
- package/dist/tenant/reset.d.ts +21 -0
- package/dist/tenant/reset.js +125 -0
- package/dist/tenant/runtime.d.ts +80 -0
- package/dist/tenant/runtime.js +2256 -0
- package/dist/tenant/schema.d.ts +23 -0
- package/dist/tenant/schema.js +151 -0
- package/dist/tenant/status.d.ts +28 -0
- package/dist/tenant/status.js +95 -0
- package/dist/tenant/store-fs.d.ts +16 -0
- package/dist/tenant/store-fs.js +205 -0
- package/dist/tenant/store-memory.d.ts +19 -0
- package/dist/tenant/store-memory.js +100 -0
- package/dist/tenant/types.d.ts +125 -0
- package/dist/tenant/types.js +1 -0
- package/dist/vault/kek.d.ts +5 -2
- package/dist/vault/kek.js +7 -10
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +9 -6
- package/dist/core/main.js +0 -35
- /package/dist/{core/main.d.ts → host/config.js} +0 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { LAUNCHER_PROTOCOL } from "@nylorun/core/compatibility";
|
|
2
|
+
import { readHostConfig } from "./host-config.js";
|
|
3
|
+
import { readHostState } from "./host-state.js";
|
|
4
|
+
import { processAlive } from "./locks.js";
|
|
5
|
+
import { hostUrl } from "./paths.js";
|
|
6
|
+
import { portBindable, probeHostHealth } from "./probe.js";
|
|
7
|
+
/**
|
|
8
|
+
* Status with no lock and no mutation (D§9.3).
|
|
9
|
+
*/
|
|
10
|
+
export async function status(paths, launcherVersion) {
|
|
11
|
+
const config = await readHostConfig(paths);
|
|
12
|
+
const stateFile = await readHostState(paths);
|
|
13
|
+
const runtimeVersion = config && typeof config.runtimeVersion === "string"
|
|
14
|
+
? config.runtimeVersion
|
|
15
|
+
: undefined;
|
|
16
|
+
const base = {
|
|
17
|
+
launcherProtocol: LAUNCHER_PROTOCOL,
|
|
18
|
+
home: paths.root,
|
|
19
|
+
state: "absent",
|
|
20
|
+
launcherVersion,
|
|
21
|
+
...(runtimeVersion ? { runtimeVersion } : {}),
|
|
22
|
+
};
|
|
23
|
+
if (!config) {
|
|
24
|
+
return base;
|
|
25
|
+
}
|
|
26
|
+
const url = hostUrl(config);
|
|
27
|
+
const withConfig = {
|
|
28
|
+
...base,
|
|
29
|
+
state: "stopped",
|
|
30
|
+
url,
|
|
31
|
+
hostId: config.hostId,
|
|
32
|
+
...(stateFile?.pid ? { pid: stateFile.pid } : {}),
|
|
33
|
+
...(stateFile?.version ? { version: stateFile.version } : {}),
|
|
34
|
+
};
|
|
35
|
+
const probe = await probeHostHealth(url);
|
|
36
|
+
const ours = probe.health !== undefined &&
|
|
37
|
+
(probe.health.hostId === undefined ||
|
|
38
|
+
probe.health.hostId === config.hostId);
|
|
39
|
+
if (probe.foreign) {
|
|
40
|
+
return { ...withConfig, state: "foreign-port" };
|
|
41
|
+
}
|
|
42
|
+
if (probe.health && ours) {
|
|
43
|
+
return {
|
|
44
|
+
...withConfig,
|
|
45
|
+
state: "running",
|
|
46
|
+
...(probe.health.version ? { version: probe.health.version } : {}),
|
|
47
|
+
...(probe.health.hostId ? { hostId: probe.health.hostId } : {}),
|
|
48
|
+
...(probe.health.pid ? { pid: probe.health.pid } : {}),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if (probe.health && !ours) {
|
|
52
|
+
return {
|
|
53
|
+
...withConfig,
|
|
54
|
+
state: "foreign-port",
|
|
55
|
+
...(probe.health.hostId ? { hostId: probe.health.hostId } : {}),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
if (stateFile && processAlive(stateFile.pid)) {
|
|
59
|
+
return { ...withConfig, state: "unresponsive", pid: stateFile.pid };
|
|
60
|
+
}
|
|
61
|
+
if (!(await portBindable(config.host, config.port))) {
|
|
62
|
+
return { ...withConfig, state: "foreign-port" };
|
|
63
|
+
}
|
|
64
|
+
return withConfig;
|
|
65
|
+
}
|
package/dist/mcp/connect.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export declare function openMcpServer(input: {
|
|
|
11
11
|
authorize?: (url: string) => Promise<AuthorizeResult>;
|
|
12
12
|
pluginRoot?: string;
|
|
13
13
|
pluginData: string;
|
|
14
|
+
childEnv?: Readonly<Record<string, string>>;
|
|
14
15
|
}): Promise<LiveConnection>;
|
|
15
16
|
export declare function listMcpTools(client: Client, input: {
|
|
16
17
|
capabilityId: string;
|
package/dist/mcp/connect.js
CHANGED
|
@@ -10,11 +10,13 @@ export async function openMcpServer(input) {
|
|
|
10
10
|
const launch = prepareStdioLaunch(input.server, {
|
|
11
11
|
pluginRoot: input.pluginRoot,
|
|
12
12
|
pluginData: input.pluginData,
|
|
13
|
+
childEnv: input.childEnv,
|
|
13
14
|
});
|
|
14
15
|
const transport = new StdioClientTransport({
|
|
15
16
|
command: launch.command,
|
|
16
17
|
args: [...launch.args],
|
|
17
18
|
cwd: launch.cwd,
|
|
19
|
+
// Full explicit env so HOME/TMPDIR from the Tenant override getDefaultEnvironment().
|
|
18
20
|
env: { ...launch.env },
|
|
19
21
|
stderr: "pipe",
|
|
20
22
|
});
|
package/dist/mcp/pool.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentManifest } from "@nylorun/core/define";
|
|
1
|
+
import type { AgentManifest, McpServerManifest } from "@nylorun/core/define";
|
|
2
2
|
import type { AuthorizeResult } from "../vault/service.js";
|
|
3
3
|
import { callMcpTool } from "./connect.js";
|
|
4
4
|
import { type McpDiagnostic, type McpSnapshot, type McpToolRecord } from "./snapshot.js";
|
|
@@ -6,7 +6,9 @@ export declare class McpPool {
|
|
|
6
6
|
private readonly options;
|
|
7
7
|
private readonly live;
|
|
8
8
|
constructor(options: {
|
|
9
|
-
|
|
9
|
+
/** Absolute Tenant `plugin-data` directory. */
|
|
10
|
+
readonly pluginData: string;
|
|
11
|
+
readonly childEnv: Readonly<Record<string, string>>;
|
|
10
12
|
readonly authorize: (sessionId: string, request: {
|
|
11
13
|
url: string;
|
|
12
14
|
serverName: string;
|
|
@@ -31,6 +33,7 @@ export declare class McpPool {
|
|
|
31
33
|
}): Promise<McpDiagnostic[]>;
|
|
32
34
|
call(input: {
|
|
33
35
|
sessionId: string;
|
|
36
|
+
agentId?: string;
|
|
34
37
|
capabilityId: string;
|
|
35
38
|
serverName: string;
|
|
36
39
|
serverToolName: string;
|
|
@@ -43,3 +46,14 @@ export declare class McpPool {
|
|
|
43
46
|
private liveKey;
|
|
44
47
|
private connectDeclared;
|
|
45
48
|
}
|
|
49
|
+
interface DeclaredServer {
|
|
50
|
+
/** Absent for the session's root agent. */
|
|
51
|
+
readonly agentId?: string;
|
|
52
|
+
/** The manifest of the agent that declares the server. */
|
|
53
|
+
readonly manifest: AgentManifest;
|
|
54
|
+
readonly capabilityId: string;
|
|
55
|
+
readonly server: McpServerManifest;
|
|
56
|
+
}
|
|
57
|
+
/** Servers of the root agent, then of each agent it uses as a tool. */
|
|
58
|
+
export declare function serversOf(manifest: AgentManifest): DeclaredServer[];
|
|
59
|
+
export {};
|
package/dist/mcp/pool.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
|
+
import { delegatesOf } from "@nylorun/core/define";
|
|
2
3
|
import { callMcpTool, diagnosticFromError, listMcpTools, openMcpServer, } from "./connect.js";
|
|
3
4
|
import { declaredToolNames, } from "./snapshot.js";
|
|
4
5
|
export class McpPool {
|
|
@@ -8,7 +9,8 @@ export class McpPool {
|
|
|
8
9
|
this.options = options;
|
|
9
10
|
}
|
|
10
11
|
async discover(input) {
|
|
11
|
-
|
|
12
|
+
// Each agent names its own tools, so collisions are checked per agent.
|
|
13
|
+
const taken = new Map();
|
|
12
14
|
const tools = [];
|
|
13
15
|
const diagnostics = [];
|
|
14
16
|
for (const declared of serversOf(input.manifest)) {
|
|
@@ -19,13 +21,16 @@ export class McpPool {
|
|
|
19
21
|
continue;
|
|
20
22
|
}
|
|
21
23
|
try {
|
|
24
|
+
if (!taken.has(declared.agentId))
|
|
25
|
+
taken.set(declared.agentId, declaredToolNames(declared.manifest));
|
|
22
26
|
const listed = await listMcpTools(opened.connection.client, {
|
|
23
27
|
capabilityId: declared.capabilityId,
|
|
24
28
|
serverName: declared.server.name,
|
|
25
|
-
taken,
|
|
29
|
+
taken: taken.get(declared.agentId),
|
|
26
30
|
});
|
|
27
|
-
tools.push(...listed.tools);
|
|
31
|
+
tools.push(...listed.tools.map((tool) => owned(declared, tool)));
|
|
28
32
|
diagnostics.push({
|
|
33
|
+
...ownerOf(declared),
|
|
29
34
|
capabilityId: declared.capabilityId,
|
|
30
35
|
serverName: declared.server.name,
|
|
31
36
|
outcome: "connected",
|
|
@@ -37,7 +42,7 @@ export class McpPool {
|
|
|
37
42
|
}
|
|
38
43
|
catch (error) {
|
|
39
44
|
await opened.connection.close().catch(() => { });
|
|
40
|
-
diagnostics.push(diagnosticFromError(declared.capabilityId, declared.server.name, error));
|
|
45
|
+
diagnostics.push(owned(declared, diagnosticFromError(declared.capabilityId, declared.server.name, error)));
|
|
41
46
|
}
|
|
42
47
|
}
|
|
43
48
|
return {
|
|
@@ -53,16 +58,17 @@ export class McpPool {
|
|
|
53
58
|
const diagnostics = [];
|
|
54
59
|
const seen = new Set();
|
|
55
60
|
for (const tool of input.tools) {
|
|
56
|
-
const key =
|
|
61
|
+
const key = this.liveKey(input.sessionId, tool.agentId, tool.capabilityId, tool.serverName);
|
|
57
62
|
if (seen.has(key))
|
|
58
63
|
continue;
|
|
59
64
|
seen.add(key);
|
|
60
|
-
if (this.live.has(
|
|
65
|
+
if (this.live.has(key))
|
|
61
66
|
continue;
|
|
62
67
|
input.signal?.throwIfAborted();
|
|
63
|
-
const declared =
|
|
68
|
+
const declared = findServer(input.manifest, tool.agentId, tool.capabilityId, tool.serverName);
|
|
64
69
|
if (!declared) {
|
|
65
70
|
diagnostics.push({
|
|
71
|
+
...(tool.agentId === undefined ? {} : { agentId: tool.agentId }),
|
|
66
72
|
capabilityId: tool.capabilityId,
|
|
67
73
|
serverName: tool.serverName,
|
|
68
74
|
outcome: "failed",
|
|
@@ -80,10 +86,10 @@ export class McpPool {
|
|
|
80
86
|
return diagnostics;
|
|
81
87
|
}
|
|
82
88
|
async call(input) {
|
|
83
|
-
const key = this.liveKey(input.sessionId, input.capabilityId, input.serverName);
|
|
89
|
+
const key = this.liveKey(input.sessionId, input.agentId, input.capabilityId, input.serverName);
|
|
84
90
|
let live = this.live.get(key);
|
|
85
91
|
if (!live) {
|
|
86
|
-
const declared =
|
|
92
|
+
const declared = findServer(input.manifest, input.agentId, input.capabilityId, input.serverName);
|
|
87
93
|
if (!declared)
|
|
88
94
|
throw new Error(`MCP server '${input.serverName}' is not declared`);
|
|
89
95
|
const opened = await this.connectDeclared(input, declared);
|
|
@@ -102,21 +108,22 @@ export class McpPool {
|
|
|
102
108
|
await Promise.all(connections.map((item) => item.connection.close().catch(() => { })));
|
|
103
109
|
}
|
|
104
110
|
remember(sessionId, declared, connection) {
|
|
105
|
-
this.live.set(this.liveKey(sessionId, declared.capabilityId, declared.server.name), {
|
|
111
|
+
this.live.set(this.liveKey(sessionId, declared.agentId, declared.capabilityId, declared.server.name), {
|
|
106
112
|
capabilityId: declared.capabilityId,
|
|
107
113
|
serverName: declared.server.name,
|
|
108
114
|
connection,
|
|
109
115
|
});
|
|
110
116
|
}
|
|
111
|
-
liveKey(sessionId, capabilityId, serverName) {
|
|
112
|
-
return JSON.stringify([sessionId, capabilityId, serverName]);
|
|
117
|
+
liveKey(sessionId, agentId, capabilityId, serverName) {
|
|
118
|
+
return JSON.stringify([sessionId, agentId ?? null, capabilityId, serverName]);
|
|
113
119
|
}
|
|
114
120
|
async connectDeclared(input, declared) {
|
|
115
121
|
try {
|
|
116
122
|
const connection = await openMcpServer({
|
|
117
123
|
server: declared.server,
|
|
118
|
-
pluginRoot: input.pluginRoots[declared
|
|
119
|
-
pluginData: join(this.options.
|
|
124
|
+
pluginRoot: input.pluginRoots[pluginKey(declared)],
|
|
125
|
+
pluginData: join(this.options.pluginData, ...pluginKey(declared).split("/")),
|
|
126
|
+
childEnv: this.options.childEnv,
|
|
120
127
|
authorize: declared.server.type === "stdio"
|
|
121
128
|
? undefined
|
|
122
129
|
: (url) => this.options.authorize(input.sessionId, {
|
|
@@ -129,15 +136,43 @@ export class McpPool {
|
|
|
129
136
|
catch (error) {
|
|
130
137
|
return {
|
|
131
138
|
ok: false,
|
|
132
|
-
diagnostic: diagnosticFromError(declared.capabilityId, declared.server.name, error),
|
|
139
|
+
diagnostic: owned(declared, diagnosticFromError(declared.capabilityId, declared.server.name, error)),
|
|
133
140
|
};
|
|
134
141
|
}
|
|
135
142
|
}
|
|
136
143
|
}
|
|
137
|
-
|
|
144
|
+
/** Servers of the root agent, then of each agent it uses as a tool. */
|
|
145
|
+
export function serversOf(manifest) {
|
|
138
146
|
const servers = [];
|
|
139
|
-
|
|
140
|
-
for (const
|
|
141
|
-
|
|
147
|
+
const add = (agent, agentId) => {
|
|
148
|
+
for (const capability of agent.capabilities)
|
|
149
|
+
for (const server of Object.values(capability.mcpServers ?? {}))
|
|
150
|
+
servers.push({
|
|
151
|
+
...(agentId === undefined ? {} : { agentId }),
|
|
152
|
+
manifest: agent,
|
|
153
|
+
capabilityId: capability.id,
|
|
154
|
+
server,
|
|
155
|
+
});
|
|
156
|
+
};
|
|
157
|
+
add(manifest);
|
|
158
|
+
for (const child of delegatesOf(manifest))
|
|
159
|
+
add(child.manifest, child.manifest.id);
|
|
142
160
|
return servers;
|
|
143
161
|
}
|
|
162
|
+
function findServer(manifest, agentId, capabilityId, serverName) {
|
|
163
|
+
return serversOf(manifest).find((item) => item.agentId === agentId &&
|
|
164
|
+
item.capabilityId === capabilityId &&
|
|
165
|
+
item.server.name === serverName);
|
|
166
|
+
}
|
|
167
|
+
/** Plugin roots of agents used as tools are registered as `<agent>/<capability>`. */
|
|
168
|
+
function pluginKey(declared) {
|
|
169
|
+
return declared.agentId === undefined
|
|
170
|
+
? declared.capabilityId
|
|
171
|
+
: `${declared.agentId}/${declared.capabilityId}`;
|
|
172
|
+
}
|
|
173
|
+
function ownerOf(declared) {
|
|
174
|
+
return declared.agentId === undefined ? {} : { agentId: declared.agentId };
|
|
175
|
+
}
|
|
176
|
+
function owned(declared, value) {
|
|
177
|
+
return { ...ownerOf(declared), ...value };
|
|
178
|
+
}
|
package/dist/mcp/snapshot.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { JsonObject } from "@nylorun/core/define";
|
|
2
2
|
import type { AgentManifest } from "@nylorun/core/define";
|
|
3
3
|
export interface McpToolRecord {
|
|
4
|
+
/** The agent used as a tool that declares the server; absent for the session's root agent. */
|
|
5
|
+
readonly agentId?: string;
|
|
4
6
|
readonly capabilityId: string;
|
|
5
7
|
readonly serverName: string;
|
|
6
8
|
readonly serverToolName: string;
|
|
@@ -15,6 +17,8 @@ export interface McpSnapshot {
|
|
|
15
17
|
readonly mcpTools: readonly McpToolRecord[];
|
|
16
18
|
}
|
|
17
19
|
export interface McpDiagnostic {
|
|
20
|
+
/** The agent used as a tool that declares the server; absent for the session's root agent. */
|
|
21
|
+
readonly agentId?: string;
|
|
18
22
|
readonly capabilityId: string;
|
|
19
23
|
readonly serverName: string;
|
|
20
24
|
readonly outcome: "connected" | "refused" | "failed";
|
package/dist/mcp/stdio.d.ts
CHANGED
|
@@ -12,4 +12,6 @@ export declare function prepareStdioLaunch(server: Extract<McpServerManifest, {
|
|
|
12
12
|
}>, options: {
|
|
13
13
|
readonly pluginRoot?: string;
|
|
14
14
|
readonly pluginData: string;
|
|
15
|
+
/** Allowlisted base env (Tenant HOME/TMPDIR/PATH). Manifest env overlays it (A9). */
|
|
16
|
+
readonly childEnv?: Readonly<Record<string, string>>;
|
|
15
17
|
}): StdioLaunch;
|
package/dist/mcp/stdio.js
CHANGED
|
@@ -38,7 +38,9 @@ export function prepareStdioLaunch(server, options) {
|
|
|
38
38
|
const cwd = server.cwd
|
|
39
39
|
? resolveContainedCwd(server.cwd, pluginRoot, pluginData)
|
|
40
40
|
: (pluginRoot ?? pluginData);
|
|
41
|
-
|
|
41
|
+
// childEnv under manifest env; HOME/TMPDIR from childEnv override Host defaults
|
|
42
|
+
// that StdioClientTransport merges via getDefaultEnvironment() (R2).
|
|
43
|
+
const env = { ...(options.childEnv ?? {}) };
|
|
42
44
|
for (const [key, value] of Object.entries(server.env ?? {}))
|
|
43
45
|
env[key] = expand(value, pluginRoot, pluginData);
|
|
44
46
|
if (pluginRoot)
|
|
@@ -3,7 +3,7 @@ export declare class ProjectCredentialStore implements CredentialStore {
|
|
|
3
3
|
#private;
|
|
4
4
|
private readonly file;
|
|
5
5
|
private readonly legacyFile?;
|
|
6
|
-
constructor(file
|
|
6
|
+
constructor(file: string, legacyFile?: string | undefined);
|
|
7
7
|
read(providerId: string): Promise<Credential | undefined>;
|
|
8
8
|
list(): Promise<readonly CredentialInfo[]>;
|
|
9
9
|
modify(providerId: string, fn: (current: Credential | undefined) => Promise<Credential | undefined>): Promise<Credential | undefined>;
|
package/dist/model/auth-store.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
var _a;
|
|
2
2
|
import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
3
|
-
import { dirname
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
4
|
export class ProjectCredentialStore {
|
|
5
5
|
file;
|
|
6
6
|
legacyFile;
|
|
7
7
|
#chain = Promise.resolve();
|
|
8
|
-
constructor(file
|
|
8
|
+
constructor(file, legacyFile) {
|
|
9
9
|
this.file = file;
|
|
10
10
|
this.legacyFile = legacyFile;
|
|
11
11
|
}
|
package/dist/model/models.d.ts
CHANGED
|
@@ -6,6 +6,10 @@ export type Selection = Readonly<{
|
|
|
6
6
|
baseUrl: string;
|
|
7
7
|
}>;
|
|
8
8
|
}>;
|
|
9
|
+
/**
|
|
10
|
+
* Build the pi-ai model registry from explicit credentials only.
|
|
11
|
+
* Ambient process environment / provider env auth is never consulted (A7, A10).
|
|
12
|
+
*/
|
|
9
13
|
export declare function modelsFor(selection: Selection, credentials: CredentialStore, options?: {
|
|
10
14
|
environment?: boolean;
|
|
11
15
|
}): import("@earendil-works/pi-ai").MutableModels;
|
package/dist/model/models.js
CHANGED
|
@@ -1,31 +1,15 @@
|
|
|
1
|
-
import { createProvider,
|
|
1
|
+
import { createProvider, } from "@earendil-works/pi-ai";
|
|
2
2
|
import { stream, streamSimple, } from "@earendil-works/pi-ai/api/openai-completions";
|
|
3
3
|
import { builtinModels } from "@earendil-works/pi-ai/providers/all";
|
|
4
|
+
/**
|
|
5
|
+
* Build the pi-ai model registry from explicit credentials only.
|
|
6
|
+
* Ambient process environment / provider env auth is never consulted (A7, A10).
|
|
7
|
+
*/
|
|
4
8
|
export function modelsFor(selection, credentials, options = {}) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
(!selection.provider || selection.provider === providerId))
|
|
10
|
-
return { type: "api_key", key: explicit };
|
|
11
|
-
const provider = models
|
|
12
|
-
.getProviders()
|
|
13
|
-
.find((item) => item.id === providerId);
|
|
14
|
-
const ambient = await provider?.auth.apiKey?.resolve({
|
|
15
|
-
ctx: defaultProviderAuthContext(),
|
|
16
|
-
signal: options?.signal ?? new AbortController().signal,
|
|
17
|
-
});
|
|
18
|
-
if (ambient)
|
|
19
|
-
return undefined;
|
|
20
|
-
return credentials.read(providerId, options);
|
|
21
|
-
},
|
|
22
|
-
list: (options) => credentials.list(options),
|
|
23
|
-
modify: (id, fn, options) => credentials.modify(id, fn, options),
|
|
24
|
-
delete: (id, options) => credentials.delete(id, options),
|
|
25
|
-
};
|
|
26
|
-
const models = builtinModels({
|
|
27
|
-
credentials: options.environment === false ? credentials : environmentFirst,
|
|
28
|
-
});
|
|
9
|
+
if (options.environment) {
|
|
10
|
+
throw new Error("Ambient model environment is not supported; pass credentials explicitly");
|
|
11
|
+
}
|
|
12
|
+
const models = builtinModels({ credentials });
|
|
29
13
|
if (!selection.custom)
|
|
30
14
|
return models;
|
|
31
15
|
const model = {
|
|
@@ -45,7 +29,19 @@ export function modelsFor(selection, credentials, options = {}) {
|
|
|
45
29
|
name: "Custom OpenAI-compatible",
|
|
46
30
|
baseUrl: selection.custom.baseUrl,
|
|
47
31
|
auth: {
|
|
48
|
-
apiKey:
|
|
32
|
+
apiKey: {
|
|
33
|
+
label: "Custom API key",
|
|
34
|
+
schemas: [],
|
|
35
|
+
async resolve() {
|
|
36
|
+
const credential = await credentials.read("custom");
|
|
37
|
+
if (!credential || credential.type !== "api_key")
|
|
38
|
+
return undefined;
|
|
39
|
+
const key = credential.key;
|
|
40
|
+
if (!key)
|
|
41
|
+
return undefined;
|
|
42
|
+
return { type: "api_key", auth: { apiKey: key } };
|
|
43
|
+
},
|
|
44
|
+
},
|
|
49
45
|
},
|
|
50
46
|
models: [model],
|
|
51
47
|
api: { stream, streamSimple },
|
package/dist/model/pi-model.js
CHANGED
|
@@ -13,7 +13,7 @@ const emptyUsage = () => ({
|
|
|
13
13
|
export function piModel(options = {}) {
|
|
14
14
|
return async (call, context) => {
|
|
15
15
|
context.signal.throwIfAborted();
|
|
16
|
-
const root = options.root ??
|
|
16
|
+
const root = options.root ?? "";
|
|
17
17
|
const stored = options.readHostModel?.();
|
|
18
18
|
if (!stored)
|
|
19
19
|
throw new Error("Model provider is not configured. Start nylorun dev in a terminal, or set it in Studio.");
|
package/dist/model/settings.d.ts
CHANGED
|
@@ -1 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Collect likely secret strings from an explicit env map and optional auth.json
|
|
3
|
+
* under `root`. Callers must pass values; no ambient environment or cwd (A7).
|
|
4
|
+
*/
|
|
5
|
+
export declare function projectSecrets(root: string, env?: Readonly<Record<string, string | undefined>>): readonly string[];
|
package/dist/model/settings.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Collect likely secret strings from an explicit env map and optional auth.json
|
|
5
|
+
* under `root`. Callers must pass values; no ambient environment or cwd (A7).
|
|
6
|
+
*/
|
|
7
|
+
export function projectSecrets(root, env = {}) {
|
|
8
|
+
const values = Object.entries(env)
|
|
5
9
|
.filter(([key]) => /key|token|secret|password|credential/i.test(key))
|
|
6
10
|
.flatMap(([, value]) => (value ? [value] : []));
|
|
7
11
|
for (const directory of [".nylorun", ".env"])
|
package/dist/sandbox/select.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export interface SandboxSelection {
|
|
|
7
7
|
readonly reason: string;
|
|
8
8
|
readonly probes: readonly SandboxProbe[];
|
|
9
9
|
}
|
|
10
|
-
/** Serializable view of a selection for the CLI and `GET /v1/
|
|
10
|
+
/** Serializable view of a selection for the CLI and `GET /v1/tenant/sandbox`. */
|
|
11
11
|
export interface SandboxSelectionReport {
|
|
12
12
|
readonly preference: SandboxPreference;
|
|
13
13
|
readonly backend: SandboxBackendName | null;
|
|
@@ -27,9 +27,10 @@ export declare function parseSandboxPreference(value: string | undefined): Sandb
|
|
|
27
27
|
export declare function selectSandboxBackend(backends: readonly SandboxBackend[], preference: SandboxPreference): Promise<SandboxSelection>;
|
|
28
28
|
export declare function reportSelection(selection: SandboxSelection): SandboxSelectionReport;
|
|
29
29
|
/**
|
|
30
|
-
* Probe
|
|
31
|
-
*
|
|
30
|
+
* Probe backends the way a Tenant Runtime would at open.
|
|
31
|
+
* Callers must pass an explicit probe root; no ambient env or OS temp (A7).
|
|
32
32
|
*/
|
|
33
33
|
export declare function probeSandboxBackends(options?: {
|
|
34
34
|
readonly preference?: string;
|
|
35
|
+
readonly root?: string;
|
|
35
36
|
}): Promise<SandboxSelectionReport>;
|
package/dist/sandbox/select.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { tmpdir } from "node:os";
|
|
2
|
-
import { join } from "node:path";
|
|
3
1
|
import { microsandboxBackend } from "../adapters/sandbox/microsandbox.js";
|
|
4
2
|
import { virtualBackend } from "../adapters/sandbox/virtual.js";
|
|
5
3
|
const PREFERENCES = ["auto", "microsandbox", "virtual"];
|
|
@@ -26,10 +24,15 @@ export async function selectSandboxBackend(backends, preference) {
|
|
|
26
24
|
const backend = backends.find((item) => item.name === preference);
|
|
27
25
|
const probe = probeOf(preference);
|
|
28
26
|
if (backend && probe?.available)
|
|
29
|
-
return {
|
|
27
|
+
return {
|
|
28
|
+
preference,
|
|
29
|
+
backend,
|
|
30
|
+
reason: `${preference} selected by TenantConfig.sandbox.backend`,
|
|
31
|
+
probes,
|
|
32
|
+
};
|
|
30
33
|
return {
|
|
31
34
|
preference,
|
|
32
|
-
reason: `
|
|
35
|
+
reason: `sandbox.backend=${preference} but ${preference} is unavailable: ${probe?.reason ?? "unknown backend"}. Fix it, or set backend to auto to use the best available backend`,
|
|
33
36
|
probes,
|
|
34
37
|
};
|
|
35
38
|
}
|
|
@@ -58,18 +61,21 @@ export function reportSelection(selection) {
|
|
|
58
61
|
};
|
|
59
62
|
}
|
|
60
63
|
/**
|
|
61
|
-
* Probe
|
|
62
|
-
*
|
|
64
|
+
* Probe backends the way a Tenant Runtime would at open.
|
|
65
|
+
* Callers must pass an explicit probe root; no ambient env or OS temp (A7).
|
|
63
66
|
*/
|
|
64
67
|
export async function probeSandboxBackends(options = {}) {
|
|
65
|
-
|
|
66
|
-
|
|
68
|
+
if (!options.root) {
|
|
69
|
+
throw new Error("probeSandboxBackends requires an explicit root (Tenant or Host tmp path)");
|
|
70
|
+
}
|
|
71
|
+
const preference = parseSandboxPreference(options.preference ?? "auto");
|
|
72
|
+
const backends = defaultSandboxBackends({ root: options.root });
|
|
67
73
|
if (!preference)
|
|
68
74
|
return {
|
|
69
75
|
preference: "auto",
|
|
70
76
|
backend: null,
|
|
71
77
|
isolation: null,
|
|
72
|
-
reason: "
|
|
78
|
+
reason: "sandbox.backend must be auto, microsandbox or virtual",
|
|
73
79
|
probes: await Promise.all(backends.map((backend) => backend.probe())),
|
|
74
80
|
};
|
|
75
81
|
return reportSelection(await selectSandboxBackend(backends, preference));
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared workflow sandbox: one sandbox per owning session, attached via
|
|
3
|
+
* PutSession.sandbox = { session }. Specs must match; keys use the owner id.
|
|
4
|
+
*/
|
|
5
|
+
import { type AgentManifest, type CapabilityManifest, type SandboxManifest, type WorkflowManifest } from "@nylorun/core/define";
|
|
6
|
+
export type SessionSandboxRef = {
|
|
7
|
+
readonly id: string;
|
|
8
|
+
readonly ownerUserId: string;
|
|
9
|
+
readonly manifest: AgentManifest | WorkflowManifest | Record<string, unknown>;
|
|
10
|
+
/** Session whose id keys the sandbox. Absent means this session owns its own. */
|
|
11
|
+
readonly sandboxOwnerId?: string | null;
|
|
12
|
+
};
|
|
13
|
+
/** Sandbox spec declared on an agent capability or a workflow manifest. */
|
|
14
|
+
export declare function sandboxSpecOf(manifest: AgentManifest | WorkflowManifest | Record<string, unknown> | undefined): SandboxManifest | undefined;
|
|
15
|
+
export declare function sandboxSpecsEqual(a: SandboxManifest | undefined, b: SandboxManifest | undefined): boolean;
|
|
16
|
+
/** Synthetic capability so SandboxManager.run can use a workflow (or shared) spec. */
|
|
17
|
+
export declare function capabilityForSandbox(sandbox: SandboxManifest): CapabilityManifest;
|
|
18
|
+
/**
|
|
19
|
+
* Resolve the session id that keys the sandbox. Walks one hop via sandboxOwnerId;
|
|
20
|
+
* cycles fall back to the starting session.
|
|
21
|
+
*/
|
|
22
|
+
export declare function owningSandboxSessionId(session: SessionSandboxRef, lookup: (id: string) => SessionSandboxRef | undefined): string;
|
|
23
|
+
export declare function sessionHasSandbox(session: SessionSandboxRef, lookup: (id: string) => SessionSandboxRef | undefined): boolean;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared workflow sandbox: one sandbox per owning session, attached via
|
|
3
|
+
* PutSession.sandbox = { session }. Specs must match; keys use the owner id.
|
|
4
|
+
*/
|
|
5
|
+
import { canonical, } from "@nylorun/core/define";
|
|
6
|
+
/** Sandbox spec declared on an agent capability or a workflow manifest. */
|
|
7
|
+
export function sandboxSpecOf(manifest) {
|
|
8
|
+
if (!manifest || typeof manifest !== "object")
|
|
9
|
+
return undefined;
|
|
10
|
+
if (manifest.kind === "workflow")
|
|
11
|
+
return manifest.sandbox;
|
|
12
|
+
const capabilities = manifest.capabilities;
|
|
13
|
+
if (!Array.isArray(capabilities))
|
|
14
|
+
return undefined;
|
|
15
|
+
for (const capability of capabilities)
|
|
16
|
+
if (capability.sandbox)
|
|
17
|
+
return capability.sandbox;
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
export function sandboxSpecsEqual(a, b) {
|
|
21
|
+
if (a === undefined && b === undefined)
|
|
22
|
+
return true;
|
|
23
|
+
if (a === undefined || b === undefined)
|
|
24
|
+
return false;
|
|
25
|
+
return canonical(a) === canonical(b);
|
|
26
|
+
}
|
|
27
|
+
/** Synthetic capability so SandboxManager.run can use a workflow (or shared) spec. */
|
|
28
|
+
export function capabilityForSandbox(sandbox) {
|
|
29
|
+
return {
|
|
30
|
+
id: "sandbox",
|
|
31
|
+
type: "agent",
|
|
32
|
+
sandbox,
|
|
33
|
+
tools: [],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Resolve the session id that keys the sandbox. Walks one hop via sandboxOwnerId;
|
|
38
|
+
* cycles fall back to the starting session.
|
|
39
|
+
*/
|
|
40
|
+
export function owningSandboxSessionId(session, lookup) {
|
|
41
|
+
const seen = new Set();
|
|
42
|
+
let current = session;
|
|
43
|
+
while (current) {
|
|
44
|
+
if (seen.has(current.id))
|
|
45
|
+
return session.id;
|
|
46
|
+
seen.add(current.id);
|
|
47
|
+
const ownerId = current.sandboxOwnerId;
|
|
48
|
+
if (!ownerId || ownerId === current.id)
|
|
49
|
+
return current.id;
|
|
50
|
+
current = lookup(ownerId);
|
|
51
|
+
}
|
|
52
|
+
return session.id;
|
|
53
|
+
}
|
|
54
|
+
export function sessionHasSandbox(session, lookup) {
|
|
55
|
+
const ownerId = owningSandboxSessionId(session, lookup);
|
|
56
|
+
const owner = ownerId === session.id ? session : lookup(ownerId) ?? session;
|
|
57
|
+
return sandboxSpecOf(owner.manifest) !== undefined || sandboxSpecOf(session.manifest) !== undefined;
|
|
58
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type TenantEnvelope } from "@nylorun/core/contracts";
|
|
2
|
+
export declare function parseEnvelope(raw: unknown, expectedId?: string): TenantEnvelope;
|
|
3
|
+
export declare function readEnvelopeFile(path: string, expectedId?: string): TenantEnvelope;
|
|
4
|
+
/** Write envelope via temp sibling then rename (atomic on the same filesystem). */
|
|
5
|
+
export declare function writeEnvelopeFile(envelopePath: string, envelope: TenantEnvelope): void;
|
|
6
|
+
export declare function envelopeNow(input: {
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
schemaVersion: number;
|
|
10
|
+
}, now?: Date): TenantEnvelope;
|