History

Fix executor beta packaging for 1Password wasm

Rhys Sullivan committed 7 months ago

Commit af3f4f91 parent 128b7776 files · +97 −2Browse files

Unified · Split

6 files
  1. .changeset/fuzzy-camels-cheer.md +5 −0
  2. .changeset/pre.json +1 −0
  3. apps/executor/CHANGELOG.md +6 −0
  4. apps/executor/package.json +1 −1
  5. apps/executor/src/distribution/artifact.ts +75 −1
  6. apps/executor/src/distribution/distribution.test.ts +9 −0
.changeset/fuzzy-camels-cheer.md+5−0
@@ -0,0 +1,5 @@
1+---
2+"executor": patch
3+---
4+
5+Fix the packaged beta so the bundled 1Password SDK can load its wasm asset after install.
.changeset/pre.json+1−0
@@ -51,6 +51,7 @@ · 50 unmodified lines
5151 },
5252 "changesets": [
5353 "funny-forks-listen",
54+ "fuzzy-camels-cheer",
5455 "tame-trains-confess"
5556 ]
5657 }
apps/executor/CHANGELOG.md+6−0
@@ -1,5 +1,11 @@
11 # executor
22
3+## 1.3.0-beta.2
4+
5+### Patch Changes
6+
7+- Fix the packaged beta so the bundled 1Password SDK can load its wasm asset after install.
8+
39 ## 1.3.0-beta.1
410
511 ### Patch Changes
apps/executor/package.json+1−1
@@ -1,6 +1,6 @@
11 {
22 "name": "executor",
3− "version": "1.3.0-beta.1",
3+ "version": "1.3.0-beta.2",
44 "description": "Local AI executor with a CLI, local API server, and web UI.",
55 "keywords": [
66 "executor",
apps/executor/src/distribution/artifact.ts+75−1
@@ -1,5 +1,5 @@
11 import { spawn } from "node:child_process";
2−import { existsSync } from "node:fs";
2+import { existsSync, readFileSync } from "node:fs";
33 import { chmod, cp, mkdir, rename, rm, writeFile } from "node:fs/promises";
44 import { createRequire } from "node:module";
55 import { dirname, join, resolve } from "node:path";
@@ -23,6 +23,12 @@ export type DistributionPackageArtifact = { · 17 unmodified lines
2323 resourcesDir: string;
2424 };
2525
26+type BundledDependency = {
27+ name: string;
28+ version: string;
29+ packageDir: string;
30+};
31+
2632 type CommandInput = {
2733 command: string;
2834 args: ReadonlyArray<string>;
@@ -90,6 +96,56 @@ const resolveQuickJsWasmPath = (): string => { · 61 unmodified lines
9096 return wasmPath;
9197 };
9298
99+const resolveInstalledPackage = (input: {
100+ packageName: string;
101+ fromPackageJsonPath: string;
102+}): BundledDependency => {
103+ const requireFromPackage = createRequire(input.fromPackageJsonPath);
104+ const packageEntrypointPath = requireFromPackage.resolve(input.packageName);
105+ let packageDir = dirname(packageEntrypointPath);
106+ let packageJsonPath = join(packageDir, "package.json");
107+
108+ while (!existsSync(packageJsonPath)) {
109+ const parentDir = dirname(packageDir);
110+ if (parentDir === packageDir) {
111+ throw new Error(`Unable to locate package.json for ${input.packageName} from ${packageEntrypointPath}`);
112+ }
113+ packageDir = parentDir;
114+ packageJsonPath = join(packageDir, "package.json");
115+ }
116+
117+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8")) as {
118+ name?: string;
119+ version?: string;
120+ };
121+
122+ if (!packageJson.name || !packageJson.version) {
123+ throw new Error(`Invalid package metadata at ${packageJsonPath}`);
124+ }
125+
126+ return {
127+ name: packageJson.name,
128+ version: packageJson.version,
129+ packageDir,
130+ };
131+};
132+
133+const resolveBundledDependencies = (): ReadonlyArray<BundledDependency> => {
134+ const onePasswordSdk = resolveInstalledPackage({
135+ packageName: "@1password/sdk",
136+ fromPackageJsonPath: join(repoRoot, "plugins/onepassword/sdk/package.json"),
137+ });
138+ const onePasswordSdkCore = resolveInstalledPackage({
139+ packageName: "@1password/sdk-core",
140+ fromPackageJsonPath: join(onePasswordSdk.packageDir, "package.json"),
141+ });
142+
143+ return [
144+ onePasswordSdk,
145+ onePasswordSdkCore,
146+ ];
147+};
148+
93149
94150 const createPackageJson = (input: {
95151 packageName: string;
@@ -105,6 +161,8 @@ const createPackageJson = (input: { · 9 unmodified lines
105161 url?: string;
106162 };
107163 license?: string;
164+ dependencies?: Readonly<Record<string, string>>;
165+ bundleDependencies?: ReadonlyArray<string>;
108166 }) => {
109167 const packageJson = {
110168 name: input.packageName,
@@ -122,6 +180,7 @@ const createPackageJson = (input: { · 11 unmodified lines
122180 },
123181 files: [
124182 "bin",
183+ "node_modules",
125184 "resources",
126185 "README.md",
127186 "package.json",
@@ -129,6 +188,8 @@ const createPackageJson = (input: { · 1 unmodified line
129188 engines: {
130189 node: ">=20",
131190 },
191+ dependencies: input.dependencies,
192+ bundleDependencies: input.bundleDependencies,
132193 };
133194
134195 return JSON.stringify(packageJson, null, 2) + "\n";
@@ -195,6 +256,7 @@ const buildCliBundle = async (input: { · 60 unmodified lines
195256 "node",
196257 "--outdir",
197258 input.binDir,
259+ "--external=@1password/sdk",
198260 ],
199261 cwd: repoRoot,
200262 });
@@ -217,7 +279,9 @@ export const buildDistributionPackage = async ( · 16 unmodified lines
217279 const webDir = join(resourcesDir, "web");
218280 const bundlePath = join(binDir, "executor.mjs");
219281 const launcherPath = join(binDir, "executor.js");
282+ const nodeModulesDir = join(packageDir, "node_modules");
220283 const quickJsWasmPath = resolveQuickJsWasmPath();
284+ const bundledDependencies = resolveBundledDependencies();
221285
222286 const webDistDir = join(repoRoot, "apps/web/dist");
223287 const readmePath = join(repoRoot, "README.md");
@@ -225,6 +289,7 @@ export const buildDistributionPackage = async ( · 1 unmodified line
225289 const packageVersion = options.packageVersion ?? defaults.version;
226290 await rm(packageDir, { recursive: true, force: true });
227291 await mkdir(binDir, { recursive: true });
292+ await mkdir(nodeModulesDir, { recursive: true });
228293 await mkdir(resourcesDir, { recursive: true });
229294
230295 if ((options.buildWeb ?? true) || !existsSync(webDistDir)) {
@@ -245,6 +310,11 @@ export const buildDistributionPackage = async ( · 14 unmodified lines
245310 });
246311
247312 await cp(webDistDir, webDir, { recursive: true });
313+ for (const dependency of bundledDependencies) {
314+ const destinationDir = join(nodeModulesDir, ...dependency.name.split("/"));
315+ await mkdir(dirname(destinationDir), { recursive: true });
316+ await cp(dependency.packageDir, destinationDir, { recursive: true });
317+ }
248318 await cp(quickJsWasmPath, join(binDir, "emscripten-module.wasm"));
249319 await cp(
250320 join(repoRoot, "packages/kernel/runtime-deno-subprocess/src/deno-subprocess-worker.mjs"),
@@ -271,6 +341,10 @@ export const buildDistributionPackage = async ( · 20 unmodified lines
271341 bugs: defaults.bugs,
272342 repository: defaults.repository,
273343 license: defaults.license,
344+ dependencies: Object.fromEntries(
345+ bundledDependencies.map((dependency) => [dependency.name, dependency.version]),
346+ ),
347+ bundleDependencies: bundledDependencies.map((dependency) => dependency.name),
274348 }));
275349 await cp(readmePath, join(packageDir, "README.md"));
276350 await writeFile(launcherPath, createLauncherSource());
apps/executor/src/distribution/distribution.test.ts+9−0
@@ -1,3 +1,6 @@
1+import { existsSync, readFileSync } from "node:fs";
2+import { join } from "node:path";
3+
14 import { describe, expect, it } from "@effect/vitest";
25 import * as Effect from "effect/Effect";
36
@@ -17,6 +20,12 @@ describe("distribution flow", () => { · 13 unmodified lines
1720 ) =>
1821 Effect.gen(function* () {
1922 const harness = yield* DistributionHarness;
23+ const bundleSource = readFileSync(join(harness.packageDir, "bin/executor.mjs"), "utf8");
24+
25+ expect(bundleSource).not.toContain("core_bg.wasm");
26+ expect(
27+ existsSync(join(harness.packageDir, "node_modules/@1password/sdk-core/nodejs/core_bg.wasm")),
28+ ).toBe(true);
2029
2130 yield* harness.writeProjectConfig(`{
2231 "runtime": "ses",