@almadar/shell-node 0.1.0 → 1.0.0
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/OrbitalServerRuntime.d.ts +46 -267
- package/dist/OrbitalServerRuntime.js +3 -3
- package/dist/chunk-3PNZYEZQ.js +844 -0
- package/dist/{chunk-QYAPNUBH.js → chunk-7QE32QQR.js} +107 -182
- package/dist/{chunk-WZFQ7T7V.js → chunk-7QG7JGKQ.js} +2 -2
- package/dist/{chunk-UBZT5LDW.js → chunk-EEYGL7AB.js} +3 -13
- package/dist/chunk-VQVAHUAX.js +183 -0
- package/dist/{chunk-BJA6GYC5.js → chunk-YLFK5OJN.js} +61 -55
- package/dist/createProgramHandlers.js +5 -5
- package/dist/index.d.ts +97 -61
- package/dist/index.js +7 -7
- package/dist/kernel-loader-3JR6LJV5.js +1 -0
- package/dist/{kernel-workspace-host-CU56iOlt.d.ts → kernel-workspace-host-cT9JGJur.d.ts} +7 -12
- package/dist/kernelWorkspace.d.ts +1 -1
- package/dist/kernelWorkspace.js +3 -3
- package/dist/program-6DB6LK5B.js +2 -0
- package/package.json +9 -7
- package/dist/chunk-23YAOI6P.js +0 -42
- package/dist/chunk-A2TIXOLJ.js +0 -1326
- package/dist/kernel-loader-24KWM5C7.js +0 -1
- package/dist/program-35OTIG6Y.js +0 -2
|
@@ -6,9 +6,9 @@ import { tmpdir } from 'os';
|
|
|
6
6
|
import { relative, resolve, isAbsolute, sep, join } from 'path';
|
|
7
7
|
import { z } from 'zod';
|
|
8
8
|
import { createLogger } from '@almadar/logger';
|
|
9
|
+
import { decodeProveReport, decodeOsGrantsReport, decodeValidationJsonOutput, isStepRefusal, decodeStepRefusal, decodePlayedStep } from '@almadar/core/kernel-wire';
|
|
9
10
|
import { resolveOrbBinary, discoverBehaviorPackages } from '@almadar/core/behavior-package';
|
|
10
|
-
import { EventPayloadSchema,
|
|
11
|
-
import { composeAppFromFiles, orbitalImportResolver, atomTraitResolver, organismOrderResolver } from '@almadar/core/builders';
|
|
11
|
+
import { EventPayloadSchema, CatalogEntrySchema, formatTraitValueLiteral, BehaviorDescriptionSchema, importsOf, dependencyGraphOf, DescribeTargetSchema, CatalogScopeSchema, parseProgram, ProgramInspectionSchema, TraitDescriptionSchema } from '@almadar/core';
|
|
12
12
|
|
|
13
13
|
var log = createLogger("almadar:integrations:program");
|
|
14
14
|
function runOrb(args, opts) {
|
|
@@ -44,6 +44,17 @@ function parseJson(schema, run, what) {
|
|
|
44
44
|
if (!parsed.success) throw new Error(`${what}: unexpected orb output: ${parsed.error.message}`);
|
|
45
45
|
return parsed.data;
|
|
46
46
|
}
|
|
47
|
+
function decodeJson(decode, run, what) {
|
|
48
|
+
if (run.code !== 0) throw new Error(`${what}: orb exited ${run.code}: ${run.stderr.trim()}`);
|
|
49
|
+
try {
|
|
50
|
+
return decode(JSON.parse(run.stdout));
|
|
51
|
+
} catch (error) {
|
|
52
|
+
throw new Error(`${what}: unexpected orb output: ${error instanceof Error ? error.message : String(error)}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function issueOf(finding) {
|
|
56
|
+
return { code: finding.code, message: finding.message, path: finding.path, ...finding.line !== void 0 ? { line: finding.line } : {}, ...finding.column !== void 0 ? { column: finding.column } : {} };
|
|
57
|
+
}
|
|
47
58
|
function withTempFile(name, content, use) {
|
|
48
59
|
const dir = mkdtempSync(join(tmpdir(), "almadar-program-"));
|
|
49
60
|
const path = join(dir, name);
|
|
@@ -57,33 +68,33 @@ function withProjectFile(cwd, name, content, use) {
|
|
|
57
68
|
}
|
|
58
69
|
async function behaviorCatalog(scope, opts, options = {}) {
|
|
59
70
|
const exposure = (scope.exposure ?? []).flatMap((e) => ["--exposure", e]);
|
|
60
|
-
const
|
|
61
|
-
const run = await runOrb(["behaviors", "catalog", "--json", ...
|
|
71
|
+
const noTraits = options.traits === false ? ["--no-traits"] : [];
|
|
72
|
+
const run = await runOrb(["behaviors", "catalog", "--json", ...noTraits, ...scope.paths, ...exposure], opts);
|
|
62
73
|
return parseJson(CatalogEntrySchema.array(), run, "behavior/catalog");
|
|
63
74
|
}
|
|
64
75
|
function behaviorCatalogSync(scope, opts) {
|
|
65
76
|
const exposure = (scope.exposure ?? []).flatMap((e) => ["--exposure", e]);
|
|
66
77
|
return parseJson(CatalogEntrySchema.array(), runOrbSync(["behaviors", "catalog", "--json", ...scope.paths, ...exposure], opts), "behavior/catalog");
|
|
67
78
|
}
|
|
68
|
-
async function behaviorDescribe(value, opts
|
|
69
|
-
const knobVectors = options.knobVectors === true ? ["--knob-vectors"] : [];
|
|
79
|
+
async function behaviorDescribe(value, opts) {
|
|
70
80
|
if ("trait" in value) {
|
|
71
|
-
const run = await runOrb(["behaviors", "describe", "--json",
|
|
81
|
+
const run = await runOrb(["behaviors", "describe", "--json", formatTraitValueLiteral(value)], opts);
|
|
72
82
|
return parseJson(TraitDescriptionSchema, run, "behavior/describe");
|
|
73
83
|
}
|
|
74
84
|
const spec = "orbital" in value ? `${value.behavior}.orbitals.${value.orbital}` : value.behavior;
|
|
75
|
-
return parseJson(BehaviorDescriptionSchema, await runOrb(["behaviors", "describe", "--json",
|
|
85
|
+
return parseJson(BehaviorDescriptionSchema, await runOrb(["behaviors", "describe", "--json", spec], opts), "behavior/describe");
|
|
76
86
|
}
|
|
77
|
-
function
|
|
87
|
+
function booleanOptions(op, raw, keys) {
|
|
78
88
|
const options = raw ?? {};
|
|
79
89
|
if (options === null || typeof options !== "object" || Array.isArray(options)) return `${op}: options must be an object`;
|
|
80
|
-
|
|
90
|
+
const out = {};
|
|
81
91
|
for (const [k, v] of Object.entries(options)) {
|
|
82
|
-
|
|
83
|
-
if (
|
|
84
|
-
|
|
92
|
+
const key = keys.find((known) => known === k);
|
|
93
|
+
if (key === void 0) return `${op}: unsupported option "${k}"`;
|
|
94
|
+
if (typeof v !== "boolean") return `${op}: ${k} must be a boolean`;
|
|
95
|
+
out[key] = v;
|
|
85
96
|
}
|
|
86
|
-
return
|
|
97
|
+
return out;
|
|
87
98
|
}
|
|
88
99
|
async function behaviorSource(value, opts) {
|
|
89
100
|
const run = await runOrb(["behaviors", "source", "--json", value.behavior], opts);
|
|
@@ -102,8 +113,8 @@ async function programReadOutcome(text, opts) {
|
|
|
102
113
|
if (run.code !== 0) return fail(`program/read: orb exited ${run.code}: ${run.stderr.trim()}`);
|
|
103
114
|
const parsed = ProgramParser.safeParse(JSON.parse(run.stdout));
|
|
104
115
|
if (parsed.success) return { ok: true, result: parsed.data };
|
|
105
|
-
const report =
|
|
106
|
-
const issues = [...report.errors ?? [], ...report.warnings ?? []];
|
|
116
|
+
const report = decodeJson(decodeValidationJsonOutput, await runOrb(["validate", "--json", "--deny-warnings", path], opts), "program/read");
|
|
117
|
+
const issues = [...report.errors ?? [], ...report.warnings ?? []].map(issueOf);
|
|
107
118
|
if (issues.length === 0) return fail(`program/read: not a program: ${parsed.error.message}`);
|
|
108
119
|
return { ok: false, error: `program/read: ${issues.length} validation issue(s): ${issues.map((e) => e.message).join("; ")}`, errors: issues };
|
|
109
120
|
});
|
|
@@ -129,18 +140,6 @@ function programPrint(program, opts) {
|
|
|
129
140
|
return run.stdout;
|
|
130
141
|
});
|
|
131
142
|
}
|
|
132
|
-
var IssueSchema = z.object({
|
|
133
|
-
code: z.string(),
|
|
134
|
-
message: z.string(),
|
|
135
|
-
path: z.string().optional(),
|
|
136
|
-
line: z.number().optional(),
|
|
137
|
-
column: z.number().optional()
|
|
138
|
-
});
|
|
139
|
-
var ValidateReportSchema = z.object({
|
|
140
|
-
valid: z.boolean(),
|
|
141
|
-
errors: z.array(IssueSchema).optional(),
|
|
142
|
-
warnings: z.array(IssueSchema).optional()
|
|
143
|
-
});
|
|
144
143
|
function workspaceAccessError(behavior, opts, context) {
|
|
145
144
|
const host = installedWorkspaceHost();
|
|
146
145
|
if (host === null || !behavior.startsWith("./")) return void 0;
|
|
@@ -169,9 +168,9 @@ async function stampAndValidate(program, opts, dir, caller) {
|
|
|
169
168
|
return { ok: false, errors: [{ code: "PROGRAM_STAMP_FAILED", message: stamp.stderr.trim() }] };
|
|
170
169
|
}
|
|
171
170
|
const validateArgs = ["validate", "--json", "--deny-warnings", ...opts.final === true ? ["--final"] : [], stamped];
|
|
172
|
-
const report =
|
|
173
|
-
const errors = report.errors ?? [];
|
|
174
|
-
const warnings = report.warnings ?? [];
|
|
171
|
+
const report = decodeJson(decodeValidationJsonOutput, await runOrb(validateArgs, opts), caller);
|
|
172
|
+
const errors = (report.errors ?? []).map(issueOf);
|
|
173
|
+
const warnings = (report.warnings ?? []).map(issueOf);
|
|
175
174
|
return { ok: true, stamped, errors, warnings, valid: report.valid && errors.length === 0 && warnings.length === 0 };
|
|
176
175
|
}
|
|
177
176
|
async function programValidate(program, opts) {
|
|
@@ -183,6 +182,17 @@ async function programValidate(program, opts) {
|
|
|
183
182
|
rmSync(dir, { recursive: true, force: true });
|
|
184
183
|
}
|
|
185
184
|
}
|
|
185
|
+
async function programProve(target, opts) {
|
|
186
|
+
if ("path" in target) return decodeJson(decodeProveReport, await runOrb(["prove", target.path], opts), "program/prove");
|
|
187
|
+
if (!PROGRAM_NAME.test(target.program.name)) throw new Error(`program/prove: program name "${target.program.name}" is not a file name`);
|
|
188
|
+
return withTempFile(`${target.program.name}.orb`, JSON.stringify(target.program), async (file) => decodeJson(decodeProveReport, await runOrb(["prove", file], opts), "program/prove"));
|
|
189
|
+
}
|
|
190
|
+
async function programOsGrants(target, opts) {
|
|
191
|
+
const flags = opts.final === true ? ["--final"] : [];
|
|
192
|
+
if ("path" in target) return decodeJson(decodeOsGrantsReport, await runOrb(["os-grants", target.path, ...flags], opts), "program/os-grants");
|
|
193
|
+
if (!PROGRAM_NAME.test(target.program.name)) throw new Error(`program/os-grants: program name "${target.program.name}" is not a file name`);
|
|
194
|
+
return withTempFile(`${target.program.name}.orb`, JSON.stringify(target.program), async (file) => decodeJson(decodeOsGrantsReport, await runOrb(["os-grants", file, ...flags], opts), "program/os-grants"));
|
|
195
|
+
}
|
|
186
196
|
async function programEval(program, opts) {
|
|
187
197
|
const dir = mkdtempSync(join(tmpdir(), "almadar-eval-"));
|
|
188
198
|
try {
|
|
@@ -224,92 +234,24 @@ async function programEval(program, opts) {
|
|
|
224
234
|
}
|
|
225
235
|
}
|
|
226
236
|
var LAYOUT_STRATEGIES = ["sidebar", "tabs", "dashboard", "wizard-flow", "single"];
|
|
227
|
-
function importSource(orbital) {
|
|
228
|
-
const ref = orbital.reference?.ref;
|
|
229
|
-
if (ref === void 0) return void 0;
|
|
230
|
-
const [alias, kind, name] = ref.split(".");
|
|
231
|
-
if (kind !== "orbitals" || name === void 0) return void 0;
|
|
232
|
-
const use = (orbital.uses ?? []).find((u) => u.as === alias);
|
|
233
|
-
return use === void 0 ? void 0 : { from: use.from, orbital: name };
|
|
234
|
-
}
|
|
235
|
-
var behaviorName = (from) => from.split("/").pop() ?? from;
|
|
236
237
|
async function programCompose(programs, options, opts) {
|
|
237
|
-
const
|
|
238
|
-
for (const program of programs) {
|
|
239
|
-
for (const orbital of program.orbitals) for (const use of orbital.uses ?? []) sources.add(use.from);
|
|
240
|
-
}
|
|
241
|
-
const loaded = /* @__PURE__ */ new Map();
|
|
242
|
-
for (const from of sources) loaded.set(behaviorName(from), await behaviorSource({ behavior: from }, opts));
|
|
243
|
-
const load = (name) => loaded.get(name) ?? null;
|
|
244
|
-
const byName = /* @__PURE__ */ new Map();
|
|
245
|
-
for (const program of programs) for (const orbital of program.orbitals) byName.set(orbital.name, orbital);
|
|
246
|
-
const catalogOrbitalOf = (name) => {
|
|
247
|
-
const orbital = byName.get(name);
|
|
248
|
-
const source = orbital === void 0 ? void 0 : importSource(orbital);
|
|
249
|
-
return source === void 0 ? void 0 : { organism: behaviorName(source.from), orbital: source.orbital };
|
|
250
|
-
};
|
|
251
|
-
const composed = composeAppFromFiles(programs, {
|
|
252
|
-
appName: options.appName,
|
|
253
|
-
...options.layout !== void 0 ? { layoutStrategy: options.layout } : {},
|
|
254
|
-
organismOf: (name) => catalogOrbitalOf(name)?.organism,
|
|
255
|
-
organismOrderOf: organismOrderResolver(catalogOrbitalOf, load),
|
|
256
|
-
atomTraitOf: atomTraitResolver(load),
|
|
257
|
-
importedOrbitalOf: orbitalImportResolver(load),
|
|
258
|
-
importedProgramOf: (orbital) => {
|
|
259
|
-
const source = importSource(orbital);
|
|
260
|
-
return source === void 0 ? void 0 : load(behaviorName(source.from)) ?? void 0;
|
|
261
|
-
}
|
|
262
|
-
});
|
|
263
|
-
return options.theme !== void 0 ? { ...composed.schema, theme: options.theme } : composed.schema;
|
|
264
|
-
}
|
|
265
|
-
async function mountTraitValue(value, opts, context) {
|
|
266
|
-
const denied = workspaceAccessError(value.behavior, opts, context);
|
|
267
|
-
if (denied !== void 0) return { ok: false, error: denied };
|
|
268
|
-
let source;
|
|
238
|
+
const dir = mkdtempSync(join(tmpdir(), "almadar-compose-"));
|
|
269
239
|
try {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
240
|
+
const files = programs.map((program, index) => {
|
|
241
|
+
const file = join(dir, `${index}.orb`);
|
|
242
|
+
writeFileSync(file, JSON.stringify(program));
|
|
243
|
+
return file;
|
|
244
|
+
});
|
|
245
|
+
const flags = [
|
|
246
|
+
"--app-name",
|
|
247
|
+
options.appName,
|
|
248
|
+
...options.layout !== void 0 ? ["--layout", options.layout] : [],
|
|
249
|
+
...options.theme !== void 0 ? ["--theme", options.theme] : []
|
|
250
|
+
];
|
|
251
|
+
return parseJson(ProgramParser, await runOrb(["compose", ...flags, ...files], opts), "program/compose");
|
|
252
|
+
} finally {
|
|
253
|
+
rmSync(dir, { recursive: true, force: true });
|
|
279
254
|
}
|
|
280
|
-
const { behavior, trait, config, ...rest } = value;
|
|
281
|
-
const overrides = config !== void 0 ? { ...rest, config: mergeCallSiteConfigOverrides({}, config) } : rest;
|
|
282
|
-
const projectRelative = behavior.startsWith("./");
|
|
283
|
-
const page = (owner.pages ?? []).find((p) => typeof p === "object" && "path" in p && isRoutePage(p) && (p.traits ?? []).some((t) => (typeof t === "string" ? t : "ref" in t ? t.ref : t.name) === trait));
|
|
284
|
-
const wrapper = {
|
|
285
|
-
name: `Mount${trait}`,
|
|
286
|
-
orbitals: [
|
|
287
|
-
{
|
|
288
|
-
name: "Mounted",
|
|
289
|
-
entity: `Bound.orbitals.${owner.name}.entity`,
|
|
290
|
-
reference: {
|
|
291
|
-
ref: `Bound.orbitals.${owner.name}`,
|
|
292
|
-
only: [trait],
|
|
293
|
-
...Object.keys(overrides).length > 0 ? { traits: { [trait]: overrides } } : {}
|
|
294
|
-
},
|
|
295
|
-
traits: [],
|
|
296
|
-
pages: [],
|
|
297
|
-
uses: [{ as: "Bound", from: behavior }]
|
|
298
|
-
}
|
|
299
|
-
]
|
|
300
|
-
};
|
|
301
|
-
const check = async (path) => {
|
|
302
|
-
const report = parseJson(ValidateReportSchema, await runOrb(["validate", "--json", "--deny-warnings", path], opts), "mount");
|
|
303
|
-
const issues = [...report.errors ?? [], ...report.warnings ?? []];
|
|
304
|
-
if (!report.valid || issues.length > 0) {
|
|
305
|
-
return { ok: false, error: `trait value ${formatTraitValueLiteral(value)} does not validate where it is bound`, errors: issues };
|
|
306
|
-
}
|
|
307
|
-
const schema = parseJson(ProgramParser, await runOrb(["resolve", path], opts), "mount");
|
|
308
|
-
const pagePath = page !== void 0 && typeof page === "object" && "path" in page ? page.path : void 0;
|
|
309
|
-
return { ok: true, mounted: { schema, orbital: "Mounted", trait: `Mounted${trait}`, ...pagePath !== void 0 ? { pagePath } : {} } };
|
|
310
|
-
};
|
|
311
|
-
const text = JSON.stringify(wrapper);
|
|
312
|
-
return projectRelative ? withProjectFile(opts.cwd, "mount.orb", text, check) : withTempFile("mount.orb", text, check);
|
|
313
255
|
}
|
|
314
256
|
var WHICH_LINE = /^(\/\S+\.(?:orb|lolo)) \((.+)\)$/;
|
|
315
257
|
async function behaviorFile(behavior, opts) {
|
|
@@ -323,21 +265,6 @@ async function behaviorFile(behavior, opts) {
|
|
|
323
265
|
if (run.code !== 0 || match === null) return { error: `behavior "${behavior}" is not installed${run.stderr.trim() ? `: ${run.stderr.trim()}` : ""}` };
|
|
324
266
|
return match[1];
|
|
325
267
|
}
|
|
326
|
-
async function mountBehaviorValue(value, opts, context) {
|
|
327
|
-
const denied = workspaceAccessError(value.behavior, opts, context);
|
|
328
|
-
if (denied !== void 0) return { ok: false, error: denied };
|
|
329
|
-
const file = await behaviorFile(value.behavior, opts);
|
|
330
|
-
if (typeof file !== "string") return { ok: false, error: file.error };
|
|
331
|
-
const report = parseJson(ValidateReportSchema, await runOrb(["validate", "--json", "--deny-warnings", file], opts), "mount");
|
|
332
|
-
const issues = [...report.errors ?? [], ...report.warnings ?? []];
|
|
333
|
-
if (!report.valid || issues.length > 0) {
|
|
334
|
-
return { ok: false, error: `behavior ${value.behavior} does not validate`, errors: issues };
|
|
335
|
-
}
|
|
336
|
-
const schema = parseJson(ProgramParser, await runOrb(["resolve", file], opts), "mount");
|
|
337
|
-
const firstPage = defaultPage(schema)?.page.path;
|
|
338
|
-
if (firstPage === void 0) return { ok: false, error: `behavior ${value.behavior} has no page to open` };
|
|
339
|
-
return { ok: true, mounted: { schema, firstPage } };
|
|
340
|
-
}
|
|
341
268
|
async function programResolve(path, opts) {
|
|
342
269
|
return parseJson(ProgramParser, await runOrb(["resolve", path], opts), "program/resolve");
|
|
343
270
|
}
|
|
@@ -395,57 +322,26 @@ var StepRequestSchema = z.object({
|
|
|
395
322
|
config: EventPayloadSchema.optional(),
|
|
396
323
|
now: z.number().optional(),
|
|
397
324
|
settleMs: z.number().optional(),
|
|
398
|
-
|
|
325
|
+
identity: z.object({ subject: z.string(), claims: EventPayloadSchema }).optional()
|
|
399
326
|
});
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
event: z.string(),
|
|
404
|
-
executed: z.boolean(),
|
|
405
|
-
previousState: z.string(),
|
|
406
|
-
newState: z.string(),
|
|
407
|
-
emits: z.array(z.object({ event: z.string(), payload: EventPayloadSchema.nullable() })),
|
|
408
|
-
firedArm: z.number().int().nullable(),
|
|
409
|
-
guards: z.array(z.object({ arm: z.number().int(), guard: SExprSchema, passed: z.boolean(), error: z.string().optional() })),
|
|
410
|
-
effects: z.array(z.object({ type: z.string(), args: z.array(SExprSchema), status: z.enum(["executed", "failed", "skipped"]), error: z.string().optional() }))
|
|
411
|
-
});
|
|
412
|
-
var StepReportSchema = z.union([
|
|
413
|
-
z.object({ success: z.literal(true), steps: z.array(z.object({ outcomes: z.array(StepOutcomeSchema), entityAfter: EventPayloadSchema.nullable() })) }),
|
|
414
|
-
z.object({ success: z.literal(false), error: z.string() })
|
|
415
|
-
]);
|
|
416
|
-
function effectsOf(effects) {
|
|
417
|
-
return effects.map((e) => ({ type: e.type, args: e.args, status: e.status, ...e.error !== void 0 ? { error: e.error } : {} }));
|
|
327
|
+
function decodePlayedSteps(printed) {
|
|
328
|
+
if (!Array.isArray(printed)) throw new Error("program/step: expected an array of played steps");
|
|
329
|
+
return printed.map((step) => decodePlayedStep(step));
|
|
418
330
|
}
|
|
419
|
-
function
|
|
420
|
-
|
|
331
|
+
function decodePlayedOutput(run) {
|
|
332
|
+
if (run.code !== 0) throw new Error(`program/step: orb exited ${run.code}: ${run.stderr.trim()}`);
|
|
333
|
+
try {
|
|
334
|
+
const printed = JSON.parse(run.stdout);
|
|
335
|
+
return isStepRefusal(printed) ? decodeStepRefusal(printed) : decodePlayedSteps(printed);
|
|
336
|
+
} catch (error) {
|
|
337
|
+
throw new Error(`program/step: unexpected orb output: ${error instanceof Error ? error.message : String(error)}`);
|
|
338
|
+
}
|
|
421
339
|
}
|
|
422
340
|
async function programStep(program, requests, opts, live = false) {
|
|
423
|
-
const steps = requests.map((r) => ({ trait: r.trait, event: r.event, payload: r.payload, from: r.from, ...r.entity !== void 0 ? { entity: r.entity } : {}, ...r.config !== void 0 ? { config: r.config } : {}, ...r.now !== void 0 ? { now: r.now } : {}, ...r.settleMs !== void 0 ? { settleMs: r.settleMs } : {}, ...r.
|
|
341
|
+
const steps = requests.map((r) => ({ orbital: r.orbital, trait: r.trait, event: r.event, payload: r.payload, from: r.from, ...r.entity !== void 0 ? { entity: r.entity } : {}, ...r.config !== void 0 ? { config: r.config } : {}, ...r.now !== void 0 ? { now: r.now } : {}, ...r.settleMs !== void 0 ? { settleMs: r.settleMs } : {}, ...r.identity !== void 0 ? { identity: r.identity } : {} }));
|
|
424
342
|
return withTempFile("program.orb", JSON.stringify(program), async (path) => {
|
|
425
|
-
const run = await runOrb(["execute", path, "--steps", JSON.stringify(steps), ...live ? ["--live"] : []], opts);
|
|
426
|
-
|
|
427
|
-
if (!report.success) return { error: report.error };
|
|
428
|
-
const results = [];
|
|
429
|
-
for (const [i, request] of requests.entries()) {
|
|
430
|
-
const moved = report.steps[i]?.outcomes ?? [];
|
|
431
|
-
const outcome = moved.find((o) => o.trait === request.trait);
|
|
432
|
-
if (outcome === void 0) return { error: `trait "${request.trait}" did not run` };
|
|
433
|
-
const firedGuarded = outcome.firedArm !== null && outcome.guards.some((g) => g.arm === outcome.firedArm);
|
|
434
|
-
results.push({ ...request, result: {
|
|
435
|
-
trait: outcome.trait,
|
|
436
|
-
event: request.event,
|
|
437
|
-
transitionFired: outcome.executed,
|
|
438
|
-
guard: firedGuarded ? "pass" : outcome.firedArm === null && outcome.guards.length > 0 ? "fail" : "none",
|
|
439
|
-
...outcome.firedArm !== null ? { firedArm: outcome.firedArm } : {},
|
|
440
|
-
state: { before: outcome.previousState, after: outcome.newState },
|
|
441
|
-
effects: effectsOf(outcome.effects),
|
|
442
|
-
emitted: emittedOf(outcome.emits, i),
|
|
443
|
-
guards: outcome.guards.map((g) => ({ arm: g.arm, guard: g.guard, passed: g.passed, trace: [], ...g.error !== void 0 ? { error: g.error } : {} })),
|
|
444
|
-
entityAfter: report.steps[i]?.entityAfter ?? null,
|
|
445
|
-
outcomes: moved.map((o) => ({ orbital: o.orbital, trait: o.trait, event: o.event, from: o.previousState, to: o.newState, arm: o.firedArm, fired: o.executed, effects: effectsOf(o.effects), emitted: emittedOf(o.emits, i) }))
|
|
446
|
-
} });
|
|
447
|
-
}
|
|
448
|
-
return results;
|
|
343
|
+
const run = await runOrb(["execute", path, "--steps", JSON.stringify(steps), "--played", ...live ? ["--live"] : []], opts);
|
|
344
|
+
return decodePlayedOutput(run);
|
|
449
345
|
});
|
|
450
346
|
}
|
|
451
347
|
function fail(error) {
|
|
@@ -465,18 +361,18 @@ async function runProgramEffect(op, args, opts, context) {
|
|
|
465
361
|
case "behavior/catalog": {
|
|
466
362
|
const scope = CatalogScopeSchema.safeParse(args[0]);
|
|
467
363
|
if (!scope.success) return fail(`behavior/catalog: ${issuesOf(scope.error)}`);
|
|
468
|
-
const
|
|
469
|
-
if (typeof
|
|
470
|
-
return { ok: true, result: await behaviorCatalog(scope.data, opts,
|
|
364
|
+
const options = booleanOptions(op, args[1], ["traits"]);
|
|
365
|
+
if (typeof options === "string") return fail(options);
|
|
366
|
+
return { ok: true, result: await behaviorCatalog(scope.data, opts, options.traits === void 0 ? {} : { traits: options.traits }) };
|
|
471
367
|
}
|
|
472
368
|
case "behavior/describe": {
|
|
473
369
|
const value = DescribeTargetSchema.safeParse(args[0]);
|
|
474
370
|
if (!value.success) return fail(`${op}: ${issuesOf(value.error)}`);
|
|
475
|
-
const
|
|
476
|
-
if (typeof
|
|
371
|
+
const none = booleanOptions(op, args[1], []);
|
|
372
|
+
if (typeof none === "string") return fail(none);
|
|
477
373
|
const denied = workspaceAccessError(value.data.behavior, opts, context);
|
|
478
374
|
if (denied !== void 0) return fail(`${op}: ${denied}`);
|
|
479
|
-
return { ok: true, result: await behaviorDescribe(value.data, opts
|
|
375
|
+
return { ok: true, result: await behaviorDescribe(value.data, opts) };
|
|
480
376
|
}
|
|
481
377
|
case "behavior/source": {
|
|
482
378
|
const value = DescribeTargetSchema.safeParse(args[0]);
|
|
@@ -564,6 +460,35 @@ async function runProgramEffect(op, args, opts, context) {
|
|
|
564
460
|
}
|
|
565
461
|
return { ok: true, result: await programValidate(program, { ...opts, ...prior !== void 0 ? { prior } : {}, ...final !== void 0 ? { final } : {} }) };
|
|
566
462
|
}
|
|
463
|
+
case "program/prove": {
|
|
464
|
+
const options = args[1] ?? {};
|
|
465
|
+
if (options === null || typeof options !== "object" || Array.isArray(options)) return fail("program/prove: options must be an object");
|
|
466
|
+
for (const key of Object.keys(options)) if (key !== "path") return fail(`program/prove: unsupported option "${key}"`);
|
|
467
|
+
const path = options["path"];
|
|
468
|
+
if (path !== void 0) {
|
|
469
|
+
if (typeof path !== "string" || !isAbsolute(path)) return fail("program/prove: path must be an absolute path");
|
|
470
|
+
return { ok: true, result: await programProve({ path }, opts) };
|
|
471
|
+
}
|
|
472
|
+
const program = programArg(op, args[0]);
|
|
473
|
+
if (typeof program === "string") return fail(program);
|
|
474
|
+
return { ok: true, result: await programProve({ program }, opts) };
|
|
475
|
+
}
|
|
476
|
+
case "program/os-grants": {
|
|
477
|
+
const options = args[1] ?? {};
|
|
478
|
+
if (options === null || typeof options !== "object" || Array.isArray(options)) return fail("program/os-grants: options must be an object");
|
|
479
|
+
for (const key of Object.keys(options)) if (key !== "path" && key !== "final") return fail(`program/os-grants: unsupported option "${key}"`);
|
|
480
|
+
const final = options["final"];
|
|
481
|
+
if (final !== void 0 && typeof final !== "boolean") return fail("program/os-grants: final must be a boolean");
|
|
482
|
+
const pass = final === true ? { final } : {};
|
|
483
|
+
const path = options["path"];
|
|
484
|
+
if (path !== void 0) {
|
|
485
|
+
if (typeof path !== "string" || !isAbsolute(path)) return fail("program/os-grants: path must be an absolute path");
|
|
486
|
+
return { ok: true, result: await programOsGrants({ path }, { ...opts, ...pass }) };
|
|
487
|
+
}
|
|
488
|
+
const program = programArg(op, args[0]);
|
|
489
|
+
if (typeof program === "string") return fail(program);
|
|
490
|
+
return { ok: true, result: await programOsGrants({ program }, { ...opts, ...pass }) };
|
|
491
|
+
}
|
|
567
492
|
case "program/eval": {
|
|
568
493
|
const program = programArg(op, args[0]);
|
|
569
494
|
if (typeof program === "string") return fail(program);
|
|
@@ -595,4 +520,4 @@ async function runProgramEffect(op, args, opts, context) {
|
|
|
595
520
|
}
|
|
596
521
|
}
|
|
597
522
|
|
|
598
|
-
export { behaviorCatalog, behaviorCatalogSync, behaviorDependencies, behaviorDescribe, behaviorResolve, behaviorSource,
|
|
523
|
+
export { behaviorCatalog, behaviorCatalogSync, behaviorDependencies, behaviorDescribe, behaviorResolve, behaviorSource, programCompose, programEval, programOsGrants, programPrint, programProve, programRead, programResolve, programResolveSchema, programStep, programValidate, runProgramEffect, workspaceAccessError };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { workspacesRoot, kernelWorkspaceHost, workspaceBucketFromEnv } from './chunk-
|
|
2
|
-
import { runProgramEffect } from './chunk-
|
|
1
|
+
import { workspacesRoot, kernelWorkspaceHost, workspaceBucketFromEnv } from './chunk-YLFK5OJN.js';
|
|
2
|
+
import { runProgramEffect } from './chunk-7QE32QQR.js';
|
|
3
3
|
import { installedWorkspaceHost, installWorkspaceHost } from './chunk-BWMLDDCL.js';
|
|
4
4
|
|
|
5
5
|
// src/program/createProgramHandlers.ts
|
|
@@ -15,9 +15,6 @@ function nativeMemoryOf(kernel) {
|
|
|
15
15
|
if (!hasMemoryStore(kernel)) throw new Error("this kernel module exports no MemoryStore; install a kernel build that carries it");
|
|
16
16
|
return new NativeMemoryPersistenceAdapter(new kernel.MemoryStore());
|
|
17
17
|
}
|
|
18
|
-
async function loadKernelModule(kernelDir, options) {
|
|
19
|
-
return loadKernel(kernelDir, options);
|
|
20
|
-
}
|
|
21
18
|
var initialised = /* @__PURE__ */ new WeakSet();
|
|
22
19
|
function initOnce(loaded, wasm, languageFrom) {
|
|
23
20
|
if (initialised.has(loaded)) return loaded;
|
|
@@ -27,17 +24,10 @@ function initOnce(loaded, wasm, languageFrom) {
|
|
|
27
24
|
initialised.add(loaded);
|
|
28
25
|
return loaded;
|
|
29
26
|
}
|
|
30
|
-
function initKernelSync(languageFrom) {
|
|
31
|
-
initOnce(installedModule, readFileSync(createRequire(import.meta.url).resolve("@almadar/kernel/kernel.wasm")), languageFrom);
|
|
32
|
-
return installedModule;
|
|
33
|
-
}
|
|
34
27
|
async function loadInstalledKernel(languageFrom) {
|
|
35
|
-
return
|
|
28
|
+
return initOnce(installedModule, readFileSync(createRequire(import.meta.url).resolve("@almadar/kernel/kernel.wasm")), languageFrom);
|
|
36
29
|
}
|
|
37
|
-
async function
|
|
38
|
-
return loadKernel(kernelDir, options);
|
|
39
|
-
}
|
|
40
|
-
async function loadKernel(kernelDir, options) {
|
|
30
|
+
async function loadKernelModule(kernelDir, options) {
|
|
41
31
|
const loaded = await import(pathToFileURL(join(kernelDir, "pkg/orbital_wasm.js")).href);
|
|
42
32
|
return initOnce(loaded, initialised.has(loaded) ? void 0 : readFileSync(join(kernelDir, "pkg/orbital_wasm_bg.wasm")), options.languageFrom);
|
|
43
33
|
}
|
|
@@ -60,4 +50,4 @@ async function loadNativeKernelModule(addonPath, options) {
|
|
|
60
50
|
return initOnce(loaded, void 0, options.languageFrom);
|
|
61
51
|
}
|
|
62
52
|
|
|
63
|
-
export {
|
|
53
|
+
export { loadInstalledKernel, loadKernelModule, loadNativeKernelModule, nativeMemoryOf, resolveNativeAddon };
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { readFile, mkdir, writeFile, rename } from 'fs/promises';
|
|
2
|
+
import { join, dirname } from 'path';
|
|
3
|
+
import { EntityRowSchema, runRowWrite } from '@almadar/core';
|
|
4
|
+
import { AsyncLocalStorage } from 'async_hooks';
|
|
5
|
+
import { installCredentialScopeStorage, CREDENTIAL_MASTER_KEY_ENV, CREDENTIAL_PREVIOUS_MASTER_KEY_ENV, CredentialVault, installCredentialStore } from '@almadar/shell-runtime';
|
|
6
|
+
import { lookup } from 'dns/promises';
|
|
7
|
+
import { request } from 'https';
|
|
8
|
+
import { isIP } from 'net';
|
|
9
|
+
|
|
10
|
+
// src/credential-rows.ts
|
|
11
|
+
var CREDENTIALS_FILE_ENV = "ALMADAR_CREDENTIALS_FILE";
|
|
12
|
+
var DEFAULT_CREDENTIALS_FILE = ".almadar/dev-credentials.json";
|
|
13
|
+
var CREDENTIAL_ROWS_ENTITY = "AlmadarIntegrationCredential";
|
|
14
|
+
function credentialsFilePath(env = process.env, cwd = process.cwd()) {
|
|
15
|
+
const named = env[CREDENTIALS_FILE_ENV];
|
|
16
|
+
return named !== void 0 && named !== "" ? named : join(cwd, DEFAULT_CREDENTIALS_FILE);
|
|
17
|
+
}
|
|
18
|
+
var gates = /* @__PURE__ */ new Map();
|
|
19
|
+
function gateOf(path) {
|
|
20
|
+
const held = gates.get(path);
|
|
21
|
+
if (held !== void 0) return held;
|
|
22
|
+
const gate = { tail: Promise.resolve() };
|
|
23
|
+
gates.set(path, gate);
|
|
24
|
+
return gate;
|
|
25
|
+
}
|
|
26
|
+
function isMissing(error) {
|
|
27
|
+
return "code" in error && error.code === "ENOENT";
|
|
28
|
+
}
|
|
29
|
+
function fileCredentialRows(path) {
|
|
30
|
+
const gate = gateOf(path);
|
|
31
|
+
const read = async () => {
|
|
32
|
+
let text;
|
|
33
|
+
try {
|
|
34
|
+
text = await readFile(path, "utf8");
|
|
35
|
+
} catch (error) {
|
|
36
|
+
if (error instanceof Error && isMissing(error)) return [];
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
let parsed;
|
|
40
|
+
try {
|
|
41
|
+
parsed = JSON.parse(text);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
throw new Error(`credential file ${path} is not JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
44
|
+
}
|
|
45
|
+
const rows = EntityRowSchema.array().safeParse(parsed);
|
|
46
|
+
if (!rows.success) throw new Error(`credential file ${path} is not an array of rows: ${rows.error.message}`);
|
|
47
|
+
const at = rows.data.findIndex((row) => typeof row.id !== "string");
|
|
48
|
+
if (at >= 0) throw new Error(`credential file ${path}: row ${at} carries no id`);
|
|
49
|
+
return rows.data;
|
|
50
|
+
};
|
|
51
|
+
const write = async (rows) => {
|
|
52
|
+
await mkdir(dirname(path), { recursive: true });
|
|
53
|
+
const temporary = `${path}.tmp`;
|
|
54
|
+
await writeFile(temporary, JSON.stringify(rows, null, 2));
|
|
55
|
+
await rename(temporary, path);
|
|
56
|
+
};
|
|
57
|
+
const change = (work) => {
|
|
58
|
+
const next = gate.tail.then(async () => {
|
|
59
|
+
const { rows, result, changed } = work(await read());
|
|
60
|
+
if (changed) await write(rows);
|
|
61
|
+
return result;
|
|
62
|
+
});
|
|
63
|
+
gate.tail = next.then(
|
|
64
|
+
() => void 0,
|
|
65
|
+
() => void 0
|
|
66
|
+
);
|
|
67
|
+
return next;
|
|
68
|
+
};
|
|
69
|
+
return {
|
|
70
|
+
async list() {
|
|
71
|
+
await gate.tail;
|
|
72
|
+
return read();
|
|
73
|
+
},
|
|
74
|
+
create: (row) => change((rows) => ({ rows: [...rows, row], result: void 0, changed: true })),
|
|
75
|
+
update: (id, data) => change((rows) => ({
|
|
76
|
+
rows: rows.map((row) => row.id === id ? { ...row, ...data, id } : row),
|
|
77
|
+
result: void 0,
|
|
78
|
+
changed: rows.some((row) => row.id === id)
|
|
79
|
+
})),
|
|
80
|
+
delete: (id) => change((rows) => {
|
|
81
|
+
const written = runRowWrite(rows, { kind: "delete", entityType: CREDENTIAL_ROWS_ENTITY, id });
|
|
82
|
+
const removed = written.outcome.kind === "deleted";
|
|
83
|
+
return { rows: written.rows, result: removed, changed: removed };
|
|
84
|
+
})
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function nodeCredentialScope() {
|
|
88
|
+
return new AsyncLocalStorage();
|
|
89
|
+
}
|
|
90
|
+
var installed = false;
|
|
91
|
+
function installNodeHostPorts() {
|
|
92
|
+
if (installed) return;
|
|
93
|
+
installed = true;
|
|
94
|
+
installCredentialScopeStorage(nodeCredentialScope());
|
|
95
|
+
}
|
|
96
|
+
async function installNodeCredentialVault(options) {
|
|
97
|
+
const env = options.env ?? process.env;
|
|
98
|
+
const master = env[CREDENTIAL_MASTER_KEY_ENV];
|
|
99
|
+
if (master === void 0) return null;
|
|
100
|
+
const previous = env[CREDENTIAL_PREVIOUS_MASTER_KEY_ENV];
|
|
101
|
+
const vault = await CredentialVault.open(
|
|
102
|
+
new options.kernel.CredentialVault(master, previous === void 0 || previous === "" ? void 0 : previous),
|
|
103
|
+
fileCredentialRows(credentialsFilePath(env, options.cwd))
|
|
104
|
+
);
|
|
105
|
+
installCredentialStore(vault);
|
|
106
|
+
return vault;
|
|
107
|
+
}
|
|
108
|
+
var IO_TIMEOUT_MS = 5e3;
|
|
109
|
+
async function systemResolve(host) {
|
|
110
|
+
return lookup(host, { all: true });
|
|
111
|
+
}
|
|
112
|
+
var TokenPostRefusal = class extends Error {
|
|
113
|
+
constructor(message) {
|
|
114
|
+
super(message);
|
|
115
|
+
this.name = "TokenPostRefusal";
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
function urlOf(endpoint) {
|
|
119
|
+
try {
|
|
120
|
+
return new URL(endpoint);
|
|
121
|
+
} catch {
|
|
122
|
+
throw new TokenPostRefusal("the token endpoint is not a url");
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function createNodeTokenPost(options) {
|
|
126
|
+
const send = options.request ?? request;
|
|
127
|
+
const resolve = options.resolve ?? systemResolve;
|
|
128
|
+
return async ({ endpoint, headers, body }) => {
|
|
129
|
+
const url = urlOf(endpoint);
|
|
130
|
+
if (url.protocol !== "https:") throw new TokenPostRefusal("the token endpoint must be https");
|
|
131
|
+
if (!options.egressAllows(endpoint)) throw new TokenPostRefusal(`${url.host} is not an origin the program declares in egress`);
|
|
132
|
+
const host = url.hostname.replace(/^\[/, "").replace(/\]$/, "");
|
|
133
|
+
const addresses = isIP(host) !== 0 ? [{ address: host, family: isIP(host) }] : await resolve(host);
|
|
134
|
+
if (addresses.length === 0) throw new TokenPostRefusal(`${host} has no addresses`);
|
|
135
|
+
const private_ = addresses.find((entry) => !options.isPublicAddress(entry.address));
|
|
136
|
+
if (private_ !== void 0) throw new TokenPostRefusal(`${host} resolves to ${private_.address}, not a public address`);
|
|
137
|
+
const checked = addresses[0];
|
|
138
|
+
const payload = Buffer.from(body, "utf8");
|
|
139
|
+
const requestHeaders = {};
|
|
140
|
+
for (const [name, value] of headers) requestHeaders[name] = value;
|
|
141
|
+
requestHeaders["Content-Length"] = payload.length;
|
|
142
|
+
requestHeaders["Connection"] = "close";
|
|
143
|
+
return new Promise((settle, reject) => {
|
|
144
|
+
const req = send(
|
|
145
|
+
{
|
|
146
|
+
method: "POST",
|
|
147
|
+
protocol: "https:",
|
|
148
|
+
host,
|
|
149
|
+
port: url.port === "" ? 443 : Number(url.port),
|
|
150
|
+
path: `${url.pathname}${url.search}`,
|
|
151
|
+
headers: requestHeaders,
|
|
152
|
+
timeout: IO_TIMEOUT_MS,
|
|
153
|
+
servername: isIP(host) === 0 ? host : void 0,
|
|
154
|
+
lookup: (_name, lookupOptions, callback) => {
|
|
155
|
+
if (lookupOptions.all === true) callback(null, [{ address: checked.address, family: checked.family }]);
|
|
156
|
+
else callback(null, checked.address, checked.family);
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
(response) => {
|
|
160
|
+
const status = response.statusCode ?? 0;
|
|
161
|
+
if (status >= 300 && status < 400) {
|
|
162
|
+
response.resume();
|
|
163
|
+
reject(new TokenPostRefusal(`the token endpoint answered a redirect (${status}); redirects are not followed`));
|
|
164
|
+
req.destroy();
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const chunks = [];
|
|
168
|
+
response.on("data", (chunk) => chunks.push(chunk));
|
|
169
|
+
response.on("end", () => settle({ status, body: Buffer.concat(chunks).toString("utf8") }));
|
|
170
|
+
response.on("error", () => reject(new TokenPostRefusal("the token endpoint response failed")));
|
|
171
|
+
}
|
|
172
|
+
);
|
|
173
|
+
req.on("timeout", () => {
|
|
174
|
+
reject(new TokenPostRefusal("the token endpoint timed out"));
|
|
175
|
+
req.destroy();
|
|
176
|
+
});
|
|
177
|
+
req.on("error", (error) => reject(new TokenPostRefusal(`could not reach the token endpoint${error.code !== void 0 ? ` (${error.code})` : ""}`)));
|
|
178
|
+
req.end(payload);
|
|
179
|
+
});
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export { DEFAULT_CREDENTIALS_FILE, TokenPostRefusal, createNodeTokenPost, credentialsFilePath, fileCredentialRows, installNodeCredentialVault, installNodeHostPorts };
|