@mobileaidev/ai-app-bridge 0.2.15 → 0.3.0-rc.2
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/LICENSE +201 -0
- package/NOTICE +7 -0
- package/README.md +306 -53
- package/bin/ai-app-bridge.js +51 -4504
- package/bin/android-permissions.js +152 -0
- package/bin/android-uia-xml.js +74 -0
- package/bin/artifact-paths.js +127 -1
- package/bin/bridge-forward.js +56 -0
- package/bin/command-discovery.js +86 -0
- package/bin/command-errors.js +60 -0
- package/bin/command-registry.js +504 -0
- package/bin/command-request.js +14 -0
- package/bin/command-router.js +80 -0
- package/bin/connection-cache.js +119 -0
- package/bin/device-provider.js +2934 -0
- package/bin/execution-host.js +596 -0
- package/bin/execution-runtime.js +101 -0
- package/bin/fact-codec.js +321 -0
- package/bin/fact-recorder.js +691 -0
- package/bin/fact-store.js +226 -0
- package/bin/feedback-probe.js +285 -0
- package/bin/intent/install-intent.js +267 -0
- package/bin/intent/intent-action-executor.js +129 -0
- package/bin/intent/intent-autonomous-adapter.js +46 -0
- package/bin/intent/intent-capture-port.js +64 -0
- package/bin/intent/intent-entry.js +226 -0
- package/bin/intent/intent-errors.js +28 -0
- package/bin/intent/intent-evidence-store.js +121 -0
- package/bin/intent/intent-lifetime.js +62 -0
- package/bin/intent/intent-observation-target.js +33 -0
- package/bin/intent/intent-observer.js +194 -0
- package/bin/intent/intent-production-adapter.js +334 -0
- package/bin/intent/intent-provider.js +27 -0
- package/bin/intent/intent-runtime.js +63 -0
- package/bin/intent/intent-worker.js +432 -0
- package/bin/intent/ios-intent-adapter.js +90 -0
- package/bin/intent/permission-intent.js +239 -0
- package/bin/intent/web-intent-adapter.js +31 -0
- package/bin/ios-device-outcome.js +47 -0
- package/bin/ios-execution.js +108 -0
- package/bin/ios-provider.js +497 -583
- package/bin/ios-runtime-binding.js +54 -0
- package/bin/ios-wda-execution.js +70 -0
- package/bin/ios-wda-port.js +98 -0
- package/bin/ios-wda-project.js +79 -0
- package/bin/mcp-server.js +83 -1040
- package/bin/mmap-scan-index.js +329 -0
- package/bin/observation-collector.js +862 -0
- package/bin/runtime-client.js +154 -0
- package/bin/runtime-directory.js +107 -0
- package/bin/runtime-protocol.js +36 -0
- package/bin/script/bounded-script-registry.js +103 -0
- package/bin/script/node-runtime-adapter.js +233 -0
- package/bin/script/progress-projector.js +63 -0
- package/bin/script/python-runtime-adapter.js +111 -0
- package/bin/script/rolling-summary.js +134 -0
- package/bin/script/script-agent-port.js +40 -0
- package/bin/script/script-assert.js +95 -0
- package/bin/script/script-capture-port.js +76 -0
- package/bin/script/script-catalog.js +85 -0
- package/bin/script/script-durable-restore.js +195 -0
- package/bin/script/script-entry-code.js +26 -0
- package/bin/script/script-entry-route.js +38 -0
- package/bin/script/script-entry.js +3 -0
- package/bin/script/script-errors.js +29 -0
- package/bin/script/script-evidence-store.js +22 -0
- package/bin/script/script-format-removed.js +26 -0
- package/bin/script/script-host-port.js +397 -0
- package/bin/script/script-ledger.js +64 -0
- package/bin/script/script-result.js +57 -0
- package/bin/script/script-sdk.js +152 -0
- package/bin/script/script-sdk.py +153 -0
- package/bin/script/script-session-channel.js +127 -0
- package/bin/script/script-spec.js +86 -0
- package/bin/script/script-supervisor.js +919 -0
- package/bin/script/templates/checkpoint-reentry.js +13 -0
- package/bin/segment-index.js +481 -0
- package/bin/segmented-fact-store.js +1571 -0
- package/bin/shared-kernel/android-h5-target.js +10 -0
- package/bin/shared-kernel/android-install-execution.js +176 -0
- package/bin/shared-kernel/android-sdk-endpoint.js +42 -0
- package/bin/shared-kernel/android-shell-execution.js +195 -0
- package/bin/shared-kernel/argument-schema.js +117 -0
- package/bin/shared-kernel/canonical-path.js +17 -0
- package/bin/shared-kernel/device-acknowledgements.js +53 -0
- package/bin/shared-kernel/device-completion-history.js +52 -0
- package/bin/shared-kernel/device-mutation-lease.js +219 -0
- package/bin/shared-kernel/device-ownership-recovery.js +95 -0
- package/bin/shared-kernel/device-ownership-store.js +94 -0
- package/bin/shared-kernel/evidence-adapters.js +251 -0
- package/bin/shared-kernel/evidence-archive.js +329 -0
- package/bin/shared-kernel/evidence-recording.js +131 -0
- package/bin/shared-kernel/evidence-schema.js +194 -0
- package/bin/shared-kernel/evidence-store.js +190 -0
- package/bin/shared-kernel/execution-admission.js +22 -0
- package/bin/shared-kernel/execution-contracts.js +171 -0
- package/bin/shared-kernel/execution-io.js +106 -0
- package/bin/shared-kernel/execution-ledger.js +125 -0
- package/bin/shared-kernel/execution-scope.js +87 -0
- package/bin/shared-kernel/execution-target.js +115 -0
- package/bin/shared-kernel/flutter-execution.js +13 -0
- package/bin/shared-kernel/flutter-h5-port.js +60 -0
- package/bin/shared-kernel/flutter-h5-target.js +9 -0
- package/bin/shared-kernel/flutter-target.js +75 -0
- package/bin/shared-kernel/h5-execution.js +11 -0
- package/bin/shared-kernel/h5-target.js +31 -0
- package/bin/shared-kernel/host-fact-store.js +49 -0
- package/bin/shared-kernel/ios-h5-target.js +9 -0
- package/bin/shared-kernel/ios-native-target.js +71 -0
- package/bin/shared-kernel/live-capture-query.js +115 -0
- package/bin/shared-kernel/managed-sdk-execution.js +78 -0
- package/bin/shared-kernel/native-execution.js +13 -0
- package/bin/shared-kernel/native-target.js +156 -0
- package/bin/shared-kernel/provider-command-contracts.js +55 -0
- package/bin/shared-kernel/recorded-payload-archive.js +195 -0
- package/bin/shared-kernel/request-context.js +35 -0
- package/bin/shared-kernel/semantic-node.js +55 -0
- package/bin/shared-kernel/summary-transformer.js +352 -0
- package/bin/shared-kernel/target-lease-protocol.js +47 -0
- package/bin/shared-kernel/text-wait.js +111 -0
- package/bin/shared-kernel/uia-execution.js +96 -0
- package/bin/shared-kernel/uia-protocol.js +214 -0
- package/bin/shared-kernel/uia-runtime-port.js +377 -0
- package/bin/shared-kernel/uia-target.js +39 -0
- package/bin/shared-kernel/web-dom-target.js +44 -0
- package/bin/shared-kernel/xml-attributes.js +25 -0
- package/bin/target-execution.js +275 -0
- package/bin/web/command-schema.js +60 -0
- package/bin/web/session-store.js +157 -0
- package/bin/web-provider.js +334 -553
- package/docs/COMMAND_CONTRACT.md +1563 -0
- package/docs/EVIDENCE_ARCHIVE.md +214 -0
- package/docs/INTENT_FOREGROUND.md +71 -0
- package/docs/INTENT_NATIVE_EDITING.md +79 -0
- package/docs/RELEASE.md +59 -0
- package/docs/SCRIPT_AUTHORING.md +489 -0
- package/node_modules/@mobileaidev/segmented-fact-store-native/LICENSE +201 -0
- package/node_modules/@mobileaidev/segmented-fact-store-native/NOTICE +7 -0
- package/node_modules/@mobileaidev/segmented-fact-store-native/binding.gyp +36 -0
- package/node_modules/@mobileaidev/segmented-fact-store-native/bindings/node/sfs_node.c +597 -0
- package/node_modules/@mobileaidev/segmented-fact-store-native/include/sfs.h +178 -0
- package/node_modules/@mobileaidev/segmented-fact-store-native/index.js +5 -0
- package/node_modules/@mobileaidev/segmented-fact-store-native/package.json +24 -0
- package/node_modules/@mobileaidev/segmented-fact-store-native/src/sfs.c +2349 -0
- package/package.json +60 -5
- package/runtime/ios-wda/AABWDABinding.h +19 -0
- package/runtime/ios-wda/AABWDABinding.m +97 -0
- package/runtime/ios-wda/AABWDAExecution.h +26 -0
- package/runtime/ios-wda/AABWDAExecution.m +172 -0
- package/runtime/ios-wda/AABWDAIntegration.h +71 -0
- package/runtime/ios-wda/AABWDAManagedRoutes.h +392 -0
- package/runtime/ios-wda/AABWDAReceiptStore.h +10 -0
- package/runtime/ios-wda/AABWDAReceiptStore.m +116 -0
- package/runtime/uia/ai-app-bridge-uia.jar +0 -0
- package/runtime/uia/manifest.json +22 -0
- package/skills/ai-app-bridge-use/SKILL.md +23 -360
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import json
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
_real_stdout = sys.stdout
|
|
7
|
+
HELD = {
|
|
8
|
+
"pause_requested",
|
|
9
|
+
"paused_manual",
|
|
10
|
+
"paused_live",
|
|
11
|
+
"paused_ambiguous",
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def send(message):
|
|
16
|
+
# Match the Host UTF-8 JSON byte budget; ASCII escaping can triple it.
|
|
17
|
+
frame = json.dumps(message, ensure_ascii=False, separators=(",", ":")) + "\n"
|
|
18
|
+
_real_stdout.buffer.write(frame.encode("utf-8", errors="backslashreplace"))
|
|
19
|
+
_real_stdout.buffer.flush()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def read_message():
|
|
23
|
+
line = sys.stdin.readline()
|
|
24
|
+
if not line:
|
|
25
|
+
return None
|
|
26
|
+
return json.loads(line)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class HostContext:
|
|
30
|
+
def __init__(self, inputs, control):
|
|
31
|
+
self.inputs = inputs
|
|
32
|
+
self._control = control
|
|
33
|
+
self._next_id = 0
|
|
34
|
+
|
|
35
|
+
def _id(self, prefix):
|
|
36
|
+
self._next_id += 1
|
|
37
|
+
return f"{prefix}{self._next_id}"
|
|
38
|
+
|
|
39
|
+
def _apply_control(self, payload):
|
|
40
|
+
if payload:
|
|
41
|
+
self._control = payload
|
|
42
|
+
|
|
43
|
+
def _hold_if_paused(self):
|
|
44
|
+
while self._control.get("status") in HELD:
|
|
45
|
+
message = read_message()
|
|
46
|
+
if message is None:
|
|
47
|
+
raise RuntimeError("channel_closed")
|
|
48
|
+
if message.get("type") == "control":
|
|
49
|
+
self._apply_control(message.get("control"))
|
|
50
|
+
continue
|
|
51
|
+
raise RuntimeError("unexpected_frame")
|
|
52
|
+
|
|
53
|
+
def _read_reply(self, message_id):
|
|
54
|
+
while True:
|
|
55
|
+
reply = read_message()
|
|
56
|
+
if reply is None:
|
|
57
|
+
raise RuntimeError("channel_closed")
|
|
58
|
+
if reply.get("type") == "control":
|
|
59
|
+
self._apply_control(reply.get("control"))
|
|
60
|
+
continue
|
|
61
|
+
if reply.get("id") == message_id:
|
|
62
|
+
self._apply_control(reply.get("control"))
|
|
63
|
+
return reply
|
|
64
|
+
raise RuntimeError("unexpected_frame")
|
|
65
|
+
|
|
66
|
+
def _sync_control(self):
|
|
67
|
+
message_id = self._id("s")
|
|
68
|
+
send({"type": "control-point", "id": message_id})
|
|
69
|
+
self._read_reply(message_id)
|
|
70
|
+
|
|
71
|
+
def _safe_point(self):
|
|
72
|
+
self._sync_control()
|
|
73
|
+
self._hold_if_paused()
|
|
74
|
+
|
|
75
|
+
def call(self, command, args=None, options=None):
|
|
76
|
+
self._safe_point()
|
|
77
|
+
message_id = self._id("c")
|
|
78
|
+
send({"type": "call", "id": message_id, "command": command, "args": args or {}, "options": options or {}})
|
|
79
|
+
reply = self._read_reply(message_id)
|
|
80
|
+
self._safe_point()
|
|
81
|
+
return reply["value"]
|
|
82
|
+
|
|
83
|
+
def assert_(self, assertion):
|
|
84
|
+
self._safe_point()
|
|
85
|
+
message_id = self._id("a")
|
|
86
|
+
send({"type": "assert", "id": message_id, "assertion": assertion})
|
|
87
|
+
reply = self._read_reply(message_id)
|
|
88
|
+
self._safe_point()
|
|
89
|
+
return reply["value"]
|
|
90
|
+
|
|
91
|
+
def progress(self, event):
|
|
92
|
+
self._safe_point()
|
|
93
|
+
send({"type": "progress", "event": event})
|
|
94
|
+
self._safe_point()
|
|
95
|
+
|
|
96
|
+
def checkpoint(self, name, state):
|
|
97
|
+
self._safe_point()
|
|
98
|
+
message_id = self._id("k")
|
|
99
|
+
send({"type": "checkpoint", "id": message_id, "name": name, "state": state})
|
|
100
|
+
reply = self._read_reply(message_id)
|
|
101
|
+
self._safe_point()
|
|
102
|
+
return reply["value"]
|
|
103
|
+
|
|
104
|
+
def askAgent(self, request):
|
|
105
|
+
self._safe_point()
|
|
106
|
+
message_id = self._id("q")
|
|
107
|
+
send({"type": "ask", "id": message_id, "request": request})
|
|
108
|
+
reply = self._read_reply(message_id)
|
|
109
|
+
self._safe_point()
|
|
110
|
+
return reply["value"]
|
|
111
|
+
|
|
112
|
+
def controlPoint(self):
|
|
113
|
+
self._sync_control()
|
|
114
|
+
return self._control
|
|
115
|
+
|
|
116
|
+
def resume(self):
|
|
117
|
+
checkpoint = self._control.get("checkpoint")
|
|
118
|
+
if not checkpoint:
|
|
119
|
+
return None
|
|
120
|
+
return checkpoint["state"]
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def load_namespace(path):
|
|
124
|
+
namespace = {}
|
|
125
|
+
with open(path, "r", encoding="utf-8") as handle:
|
|
126
|
+
exec(handle.read(), namespace, namespace)
|
|
127
|
+
return namespace
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def main():
|
|
131
|
+
artifact_path = sys.argv[1]
|
|
132
|
+
send({"type": "ready"})
|
|
133
|
+
start = read_message()
|
|
134
|
+
sys.stdout = sys.stderr
|
|
135
|
+
ctx = HostContext(start.get("inputs") or {}, start.get("control") or {"status": "running", "pauseReason": None})
|
|
136
|
+
entry = start.get("entrypoint") or "main"
|
|
137
|
+
try:
|
|
138
|
+
namespace = load_namespace(artifact_path)
|
|
139
|
+
loaded = namespace.get(entry)
|
|
140
|
+
if not callable(loaded):
|
|
141
|
+
send({"type": "fail", "error": "unsupported_entrypoint"})
|
|
142
|
+
sys.exit(1)
|
|
143
|
+
result = loaded(ctx)
|
|
144
|
+
if asyncio.iscoroutine(result):
|
|
145
|
+
result = asyncio.run(result)
|
|
146
|
+
send({"type": "return", "result": result})
|
|
147
|
+
except Exception as error:
|
|
148
|
+
send({"type": "fail", "error": str(error)})
|
|
149
|
+
sys.exit(1)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
if __name__ == "__main__":
|
|
153
|
+
main()
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const MAX_FRAME_BYTES = 1024 * 1024;
|
|
4
|
+
|
|
5
|
+
function createScriptSessionChannel(child, { maxFrameBytes = MAX_FRAME_BYTES } = {}) {
|
|
6
|
+
let buffer = '';
|
|
7
|
+
let closed = false;
|
|
8
|
+
let failure = null;
|
|
9
|
+
let killTimer;
|
|
10
|
+
child.once('exit', () => clearTimeout(killTimer));
|
|
11
|
+
const waiters = [];
|
|
12
|
+
const pending = [];
|
|
13
|
+
const replies = new Map();
|
|
14
|
+
const maxPending = 32;
|
|
15
|
+
|
|
16
|
+
if (child.stderr) {
|
|
17
|
+
child.stderr.on('data', () => {});
|
|
18
|
+
}
|
|
19
|
+
child.stdout.setEncoding('utf8');
|
|
20
|
+
child.stdout.on('data', (chunk) => {
|
|
21
|
+
if (closed) return;
|
|
22
|
+
buffer += chunk;
|
|
23
|
+
let index = buffer.indexOf('\n');
|
|
24
|
+
while (index >= 0) {
|
|
25
|
+
const line = buffer.slice(0, index);
|
|
26
|
+
buffer = buffer.slice(index + 1);
|
|
27
|
+
index = buffer.indexOf('\n');
|
|
28
|
+
if (Buffer.byteLength(line, 'utf8') > maxFrameBytes) {
|
|
29
|
+
fail(new Error('frame_too_large'));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (!line) continue;
|
|
33
|
+
let message;
|
|
34
|
+
try {
|
|
35
|
+
message = JSON.parse(line);
|
|
36
|
+
} catch {
|
|
37
|
+
fail(new Error('malformed_frame'));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (message == null || typeof message !== 'object' || Array.isArray(message)) {
|
|
41
|
+
fail(new Error('malformed_frame'));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (message.id && replies.has(message.id)) {
|
|
45
|
+
const resolve = replies.get(message.id);
|
|
46
|
+
replies.delete(message.id);
|
|
47
|
+
resolve(message);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (waiters.length > 0) {
|
|
51
|
+
waiters.shift()(message);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (pending.length >= maxPending) {
|
|
55
|
+
fail(new Error('channel_backlog'));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
pending.push(message);
|
|
59
|
+
}
|
|
60
|
+
if (Buffer.byteLength(buffer, 'utf8') > maxFrameBytes) {
|
|
61
|
+
fail(new Error('frame_too_large'));
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
child.stdout.on('end', () => fail(new Error('channel_closed')));
|
|
65
|
+
child.on('error', (error) => fail(error));
|
|
66
|
+
|
|
67
|
+
function send(message) {
|
|
68
|
+
if (closed || !child.stdin.writable) return;
|
|
69
|
+
const line = `${JSON.stringify(message)}\n`;
|
|
70
|
+
if (Buffer.byteLength(line, 'utf8') > maxFrameBytes) {
|
|
71
|
+
fail(new Error('frame_too_large'));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
child.stdin.write(line);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function nextMessage() {
|
|
78
|
+
return new Promise((resolve) => {
|
|
79
|
+
if (closed) {
|
|
80
|
+
resolve(failure);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (pending.length > 0) {
|
|
84
|
+
resolve(pending.shift());
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
waiters.push(resolve);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function waitReply(id) {
|
|
92
|
+
return new Promise((resolve) => {
|
|
93
|
+
if (closed) {
|
|
94
|
+
resolve(failure);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (replies.size >= maxPending) {
|
|
98
|
+
fail(new Error('channel_backlog'));
|
|
99
|
+
resolve({ type: 'fail', error: 'channel_backlog' });
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
replies.set(id, resolve);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function fail(error) {
|
|
107
|
+
if (closed) return;
|
|
108
|
+
closed = true;
|
|
109
|
+
const frame = { type: 'fail', error: error.message || String(error) };
|
|
110
|
+
failure = frame;
|
|
111
|
+
pending.length = 0;
|
|
112
|
+
buffer = '';
|
|
113
|
+
while (waiters.length > 0) waiters.shift()(frame);
|
|
114
|
+
for (const resolve of replies.values()) resolve(frame);
|
|
115
|
+
replies.clear();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function stop() {
|
|
119
|
+
fail(new Error('stopped'));
|
|
120
|
+
if (!child.pid) return;
|
|
121
|
+
if (child.kill('SIGTERM') && !killTimer) killTimer = setTimeout(() => child.kill('SIGKILL'), 250);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return { send, nextMessage, waitReply, stop };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
module.exports = { createScriptSessionChannel };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const { requestDirectory, resolveRequestPath } = require('../shared-kernel/request-context');
|
|
5
|
+
const { checksumOf } = require('../shared-kernel/evidence-schema');
|
|
6
|
+
const { scriptError } = require('./script-errors');
|
|
7
|
+
const { DEFAULT_PERMISSIONS } = require('./script-catalog');
|
|
8
|
+
const { scriptPermissions } = require('../command-registry');
|
|
9
|
+
const { validateValue } = require('../shared-kernel/argument-schema');
|
|
10
|
+
const { scriptSpecSchema } = require('../shared-kernel/execution-contracts');
|
|
11
|
+
const { CommandError } = require('../command-errors');
|
|
12
|
+
const { normalizeExecutionTarget } = require('../shared-kernel/execution-target');
|
|
13
|
+
|
|
14
|
+
const SCHEMA = 'aab.code-script/v1';
|
|
15
|
+
const SCRIPT_SDK_VERSION = 'aab.script-sdk/v1';
|
|
16
|
+
|
|
17
|
+
function isCodeScript(input) {
|
|
18
|
+
return Boolean(input && typeof input === 'object' && input.schemaVersion === SCHEMA);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function compileScriptSpec(input = {}) {
|
|
22
|
+
if (!isCodeScript(input)) return scriptError('invalid_script', { field: 'schemaVersion' });
|
|
23
|
+
try { validateValue(input, scriptSpecSchema(Object.keys(scriptPermissions))); }
|
|
24
|
+
catch (error) {
|
|
25
|
+
if (!(error instanceof CommandError)) throw error;
|
|
26
|
+
return scriptError(error.code, { field: error.field, message: error.message,
|
|
27
|
+
...(error.details ? { details: error.details } : {}), dispatched: false, ambiguous: false });
|
|
28
|
+
}
|
|
29
|
+
let target;
|
|
30
|
+
try { target = normalizeExecutionTarget(input.target ?? null, { nullable: true }); }
|
|
31
|
+
catch (error) { return scriptError(error.code, { field: error.field, message: error.message, dispatched: false, ambiguous: false }); }
|
|
32
|
+
const language = input.language;
|
|
33
|
+
const hasPath = Object.hasOwn(input, 'sourcePath');
|
|
34
|
+
let source = input.source;
|
|
35
|
+
if (hasPath) {
|
|
36
|
+
try {
|
|
37
|
+
source = fs.readFileSync(input.sourcePath, 'utf8');
|
|
38
|
+
} catch (error) {
|
|
39
|
+
return scriptError('source_read_failed', { field: 'sourcePath', message: error.message });
|
|
40
|
+
}
|
|
41
|
+
if (!source) return scriptError('invalid_script', { field: 'sourcePath' });
|
|
42
|
+
}
|
|
43
|
+
const spec = {
|
|
44
|
+
schemaVersion: SCHEMA,
|
|
45
|
+
name: input.name ?? 'script',
|
|
46
|
+
language,
|
|
47
|
+
cwd: resolveRequestPath(input.cwd ?? requestDirectory()),
|
|
48
|
+
source,
|
|
49
|
+
sourcePath: hasPath ? input.sourcePath : null,
|
|
50
|
+
entrypoint: input.entrypoint ?? 'main',
|
|
51
|
+
target,
|
|
52
|
+
inputs: structuredClone(input.inputs ?? {}),
|
|
53
|
+
permissions: [...(input.permissions ?? DEFAULT_PERMISSIONS)],
|
|
54
|
+
policy: {
|
|
55
|
+
timeoutMs: input.policy?.timeoutMs ?? 600_000,
|
|
56
|
+
restartPolicy: input.policy?.restartPolicy ?? 'none',
|
|
57
|
+
maxOutputBytes: input.policy?.maxOutputBytes ?? 1_048_576,
|
|
58
|
+
maxProgressBytes: input.policy?.maxProgressBytes ?? 1_048_576,
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
if (Buffer.byteLength(JSON.stringify(spec.inputs), 'utf8') > spec.policy.maxOutputBytes) {
|
|
62
|
+
return scriptError('invalid_script', { field: 'inputs' });
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
ok: true,
|
|
66
|
+
spec,
|
|
67
|
+
hash: scriptHash(spec),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function scriptHash(spec) {
|
|
72
|
+
return checksumOf({
|
|
73
|
+
source: spec.source,
|
|
74
|
+
sourcePath: spec.sourcePath,
|
|
75
|
+
cwd: spec.cwd,
|
|
76
|
+
inputs: spec.inputs,
|
|
77
|
+
policy: spec.policy,
|
|
78
|
+
language: spec.language,
|
|
79
|
+
entrypoint: spec.entrypoint,
|
|
80
|
+
sdkVersion: SCRIPT_SDK_VERSION,
|
|
81
|
+
target: spec.target,
|
|
82
|
+
permissions: spec.permissions,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
module.exports = { SCHEMA, SCRIPT_SDK_VERSION, isCodeScript, compileScriptSpec, scriptHash };
|