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, rerunnode 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 inbundle/index.htmlif 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.jscan check JavaScript syntax before committing. Packing static files does not execute or validate them. - Packing fails: read the terminal error. Check that
bundle/index.htmlexists 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_URLagainst the address it prints. - 401: run this script from the same
my-oysterproject 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.