Skip to content
Fungi
Esc
↑↓navigate↵open⌘Jpreview
On this page
Build with Fungi

Make your own App

Copy Hello into a separate App, commit it, and open it alongside Hello.

Make your own App

Copy Hello into a separate App, commit it, and open it alongside Hello.

Start with the local trial and edit Hello. Keep Oyster running in its terminal. In a second terminal, start from the folder containing my-oyster. You need Node.js and Git, but no account or payment. These commands use the Oyster package already installed in your trial project.

Create My App

Copy the working bundle, including its readiness script and styles, into a new Git repository. The block stops if any command fails, including when my-app already exists. To keep working on an existing App, use Open and change it below; choose another folder for a new App.

cd my-oyster && (
set -e
mkdir my-app
cp -R hello-app/bundle my-app/bundle
node --input-type=module <<'JS'
import { writeFileSync } from "node:fs";
writeFileSync("my-app/package.json", JSON.stringify({
  name: "my-app", private: true,
  fungi: { app: { displayName: "My App", icon: "fungi", capabilities: [] } }
}, null, 2) + "\n");
JS
cat > my-app/bundle/index.html <<'HTML'
<!doctype html>
<html lang="en" data-theme="latte">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>My App</title>
  <link rel="stylesheet" href="./lesson.css">
</head>
<body>
  <main>
    <h1>This is my own App</h1>
    <p>Edit my-app/bundle/index.html to make this page yours. Keep the main.js script: it tells Oyster this App is ready.</p>
    <p>From my-oyster, commit your edit and install it:</p>
    <pre><code>git -C my-app -c user.name=Me -c user.email=me@example.invalid commit -am "Update My App"
node install-my-app.mjs</code></pre>
    <p>Close this window, refresh the desktop, and open My App again. Oyster can stay running.</p>
  </main>
  <script type="module" src="./main.js"></script>
</body>
</html>
HTML
git -C my-app init -q
git -C my-app add package.json bundle
git -C my-app -c user.name=Me -c user.email=me@example.invalid commit -qm "Create My App"
)

Expected: my-oyster/my-app has its own first commit. Hello is still a separate App. Keep bundle/main.js and its script tag in the page: they tell the desktop that the App is ready.

Pack and install

Stay in my-oyster. Create this script once, then run it again after each committed edit. If you started the trial on a custom port, first set export OYSTER_URL=http://127.0.0.1:YOUR_PORT to its printed address.

cat > install-my-app.mjs <<'JS'
import { execFileSync } from "node:child_process";
import { mkdtemp, readFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { packRelease } from "@fungi.computer/oyster/pack";

const source = resolve("my-app");
const commit = execFileSync("git", ["-C", source, "rev-parse", "HEAD"], {
  encoding: "utf8",
}).trim();
const out = await mkdtemp(join(tmpdir(), "my-app-release-"));
try {
  console.log("Packing My App from " + commit);
  await packRelease({
    appId: "app_myapp",
    sourceDirectory: source,
    sourceRevision: `git:${commit}`,
    recipe: {
      kind: "bundled-html", entry: "bundle/index.html",
      assets: {
        maxManifestBytes: 65_536, maxObjects: 16,
        maxObjectBytes: 1_048_576, maxTotalBytes: 4_194_304,
      },
      assetBase: "./", backend: null, maxOutputBytes: 4_194_304,
    },
    outDirectory: join(out, "release"),
  });
  execFileSync("npx", ["oyster", "install", join(out, "release"), "--installation-id", "my-app"], {
    stdio: "inherit",
    env: {
      ...process.env,
      OYSTER_URL: process.env.OYSTER_URL ?? "http://127.0.0.1:8787",
      OYSTER_OWNER_KEY: (await readFile(".oyster-owner", "utf8")).trim(),
    },
  });
} catch (error) {
  console.error(error instanceof Error ? error.message : String(error));
  console.error("Check the committed bundle: git -C my-app ls-tree -r --name-only HEAD");
  console.error("Restore any missing bundle/index.html or main.js, commit, then run node install-my-app.mjs again.");
  console.error("For connection or sign-in errors, check the trial terminal and .oyster-owner in this project.");
  process.exitCode = 1;
} finally {
  await rm(out, { recursive: true, force: true });
}
JS
node install-my-app.mjs

Expected: Packing My App, Oyster’s installation progress, then a JSON result containing "installationId":"my-app" and an Open link. The script uses npx oyster install for both first installs and updates; exit code 0 means it succeeded. This packs files that are already built; it does not run a compiler or npm run build.

Open and change it

Return to the signed-in desktop, refresh the desktop to see the new installation, and open My App. Check that This is my own App is visible with no loading or error overlay. Hello should still open too. An install success alone does not prove the App is visible.

Edit my-app/bundle/index.html, keeping its readiness script, then:

git -C my-app -c user.name=Me -c user.email=me@example.invalid commit -am "Update My App"
node install-my-app.mjs

Close My App’s window, refresh the desktop, and open My App again to see the installed Release. Oyster can stay running throughout this loop. Uncommitted changes do not appear.

If it fails

  • Old text: run git -C my-app status --short. Save and commit the edited files, rerun node install-my-app.mjs, then reopen the App.
  • Window waits or times out: check the browser console for a script error. Restore the starter readiness script with cp hello-app/bundle/main.js my-app/bundle/main.js, and restore its script tag in bundle/index.html if missing. Commit the repair, rerun the install script, and reopen My App.
  • JavaScript syntax error: fix the file and line reported by the browser console. node --check my-app/bundle/main.js can check JavaScript syntax before committing. Packing static files does not execute or validate them.
  • Packing fails: read the terminal error. Check that bundle/index.html exists in the committed tree (git -C my-app ls-tree -r --name-only HEAD). Restore a missing entry, commit it, and rerun the script. No new installation is requested until packing succeeds; your previous Release stays installed.
  • Connection refused: keep the trial terminal running and check OYSTER_URL against the address it prints.
  • 401: run this script from the same my-oyster project as the running trial so it reads that project’s .oyster-owner. Do not share that key.

For a typed backend and a build pipeline, continue with Build Apps.

Last updated on

Was this page helpful?