openpouch 0.2.2 → 0.2.3

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.
Files changed (3) hide show
  1. package/README.md +3 -3
  2. package/openpouch.js +59 -10
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -10,12 +10,12 @@ npx openpouch deploy
10
10
 
11
11
  You get a live `https://<slug>.openpouch.sh` URL plus a claim link. The agent deploys autonomously; a human claims it via the link to keep it. openpouch writes the deployment truth (`deploy.manifest.json`, `deploy.evidence.json`, `DEPLOYMENT.md`) back into your repo, so any agent can resume after context loss.
12
12
 
13
- > **Framework frontends (React/Vite/Next/Svelte…): deploy the _built output_, not the source folder.** `openpouch deploy` ships whatever folder you run it in, as-is. So build first, then deploy the build directory:
13
+ > **Framework frontends (React/Vite/Next/Svelte…): deploy the _built output_, not the source folder.** Build first, then point `deploy` at the build directory:
14
14
  > ```bash
15
15
  > npm run build
16
- > cd dist && npx openpouch deploy # Vite → dist/ · CRA → build/ · Next static export → out/
16
+ > npx openpouch deploy dist # Vite → dist/ · CRA → build/ · Next static export → out/
17
17
  > ```
18
- > A raw, unbuilt source folder is served as static files (the app won't run). Server-side build-on-deploy is on the roadmap.
18
+ > If you run `openpouch deploy` in an unbuilt frontend root, it **stops and tells you which folder to deploy** instead of silently shipping source that won't run (use `openpouch deploy .` to force the current folder). Server-side build-on-deploy is on the roadmap.
19
19
 
20
20
  ## Accounts (optional)
21
21
 
package/openpouch.js CHANGED
@@ -5129,7 +5129,7 @@ async function approveCommand(ctx, requestId) {
5129
5129
  }
5130
5130
 
5131
5131
  // packages/cli/src/deploy-engine.ts
5132
- var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
5132
+ var sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
5133
5133
  var TERMINAL = /* @__PURE__ */ new Set(["live", "failed", "canceled"]);
5134
5134
  async function runSmokeChecks(url, healthcheck) {
5135
5135
  const check = healthcheck ?? { path: "/", expectStatus: 200, timeoutMs: 1e4 };
@@ -5363,7 +5363,7 @@ function renderAgentsSection(projectName) {
5363
5363
  "",
5364
5364
  `${projectName} deploys via openpouch \u2014 an agent-native deployment control plane. This works the same from any agent harness.`,
5365
5365
  "",
5366
- "- **Framework frontends (React/Vite/Next/Svelte\u2026): deploy the _built output_, not the source folder.** `openpouch deploy` ships whatever directory you run it in, as-is \u2014 so build first, then deploy the build directory: `npm run build`, then `cd dist && openpouch deploy` (Vite\u2192`dist/`, CRA\u2192`build/`, Next static export\u2192`out/`). A raw, unbuilt source folder is served as static files (the app won't run); server-side build-on-deploy is on the roadmap.",
5366
+ "- **Framework frontends (React/Vite/Next/Svelte\u2026): deploy the _built output_, not the source folder.** Build first, then point deploy at the build directory: `npm run build`, then `openpouch deploy dist` (Vite\u2192`dist/`, CRA\u2192`build/`, Next static export\u2192`out/`). Running `openpouch deploy` in an unbuilt frontend root is refused with the right folder to use (`openpouch deploy .` forces the current folder); server-side build-on-deploy is on the roadmap.",
5367
5367
  "- **Deployment truth** (what is live, where, which commit, rollback anchor): read `DEPLOYMENT.md` or `deploy.evidence.json`. Config lives in `deploy.manifest.json`, agent permissions in `deploy.policy.json`.",
5368
5368
  "- **CLI:** `openpouch inspect` \xB7 `plan` \xB7 `preview` (autonomous if policy allows) \xB7 `prod` \xB7 `verify` \xB7 `logs` \xB7 `rollback`. Add `--json` for a single machine-readable JSON object; errors come as `{category, message, fix}` \u2014 act on the fix. Exit codes are documented API.",
5369
5369
  "- **Relay the `summary` to your human:** every result (CLI `--json` and MCP) carries a top-level `summary` \u2014 plain-language, jargon-free text written for a non-technical operator. Pass it on verbatim: it says what happened, whether the app is live and healthy, the live link, and what (if anything) the human needs to do. The **live URL is the primary result** \u2014 on a successful deploy it's the top-level `url` field, ready to share.",
@@ -5628,8 +5628,8 @@ async function initCommand(ctx, flags) {
5628
5628
 
5629
5629
  // packages/cli/src/commands/instant.ts
5630
5630
  import { spawn } from "node:child_process";
5631
- import { readFile as readFile7 } from "node:fs/promises";
5632
- import { basename as basename2, join as join7 } from "node:path";
5631
+ import { readFile as readFile7, stat } from "node:fs/promises";
5632
+ import { basename as basename2, join as join7, resolve } from "node:path";
5633
5633
  var TAR_EXCLUDES = [
5634
5634
  "./node_modules",
5635
5635
  "./.git",
@@ -5642,7 +5642,7 @@ var TAR_EXCLUDES = [
5642
5642
  "./.openpouch"
5643
5643
  ];
5644
5644
  function buildTarball(cwd) {
5645
- return new Promise((resolve, reject) => {
5645
+ return new Promise((resolve2, reject) => {
5646
5646
  const args = ["-czf", "-", ...TAR_EXCLUDES.flatMap((e) => ["--exclude", e]), "-C", cwd, "."];
5647
5647
  const tar = spawn("tar", args, { stdio: ["ignore", "pipe", "pipe"] });
5648
5648
  const chunks = [];
@@ -5651,7 +5651,7 @@ function buildTarball(cwd) {
5651
5651
  tar.stderr.on("data", (c) => stderr += String(c));
5652
5652
  tar.on("error", reject);
5653
5653
  tar.on("close", (code) => {
5654
- if (code === 0) resolve(Buffer.concat(chunks));
5654
+ if (code === 0) resolve2(Buffer.concat(chunks));
5655
5655
  else reject(new Error(`tar exited ${code}: ${stderr.trim().slice(0, 300)}`));
5656
5656
  });
5657
5657
  });
@@ -5664,7 +5664,49 @@ async function projectHint(cwd) {
5664
5664
  }
5665
5665
  return basename2(cwd) || "joey";
5666
5666
  }
5667
- async function instantCommand(ctx) {
5667
+ async function unbuiltFrontendHint(dir) {
5668
+ const buildDirs = [];
5669
+ for (const c of ["dist", "build", "out"]) {
5670
+ try {
5671
+ if ((await stat(join7(dir, c))).isDirectory()) buildDirs.push(c);
5672
+ } catch {
5673
+ }
5674
+ }
5675
+ let html;
5676
+ try {
5677
+ html = await readFile7(join7(dir, "index.html"), "utf8");
5678
+ } catch {
5679
+ html = void 0;
5680
+ }
5681
+ const devEntry = html !== void 0 && (/src=["']\/?src\//.test(html) || /\.(jsx|tsx|ts|vue|svelte)["']/.test(html));
5682
+ let noEntrySpa = false;
5683
+ if (html === void 0) {
5684
+ try {
5685
+ const pkg = JSON.parse(await readFile7(join7(dir, "package.json"), "utf8"));
5686
+ if (pkg.scripts?.["build"]) {
5687
+ try {
5688
+ noEntrySpa = (await stat(join7(dir, "src"))).isDirectory();
5689
+ } catch {
5690
+ noEntrySpa = false;
5691
+ }
5692
+ }
5693
+ } catch {
5694
+ }
5695
+ }
5696
+ return { buildDirs, isUnbuilt: devEntry || noEntrySpa };
5697
+ }
5698
+ async function instantCommand(ctx, dir) {
5699
+ const explicitDir = dir !== void 0 && dir !== "";
5700
+ const targetDir = explicitDir ? resolve(ctx.cwd, dir) : ctx.cwd;
5701
+ if (explicitDir) {
5702
+ try {
5703
+ if (!(await stat(targetDir)).isDirectory()) {
5704
+ return errorResult(EXIT.USAGE, "usage", `not a directory: ${dir}`, "Pass a folder to deploy, e.g. `openpouch deploy dist`.");
5705
+ }
5706
+ } catch {
5707
+ return errorResult(EXIT.USAGE, "usage", `folder not found: ${dir}`, "Pass an existing folder, e.g. `openpouch deploy dist`.");
5708
+ }
5709
+ }
5668
5710
  const loaded = await loadManifest(ctx.cwd);
5669
5711
  if (loaded.status === "ok") {
5670
5712
  const envs = Object.values(loaded.manifest.environments ?? {});
@@ -5678,9 +5720,16 @@ async function instantCommand(ctx) {
5678
5720
  );
5679
5721
  }
5680
5722
  }
5723
+ if (!explicitDir) {
5724
+ const { buildDirs, isUnbuilt } = await unbuiltFrontendHint(targetDir);
5725
+ if (isUnbuilt) {
5726
+ const fix = buildDirs.length > 0 ? `This looks like an unbuilt frontend. Deploy the build output instead: \`openpouch deploy ${buildDirs[0]}\` \u2014 or \`openpouch deploy .\` to ship this folder as-is.` : "This looks like an unbuilt frontend (index.html references source modules). Build first, then deploy the build folder: `npm run build`, then `openpouch deploy dist` \u2014 or `openpouch deploy .` to ship this folder as-is.";
5727
+ return errorResult(EXIT.USAGE, "usage", "refusing to deploy what looks like unbuilt frontend source", fix);
5728
+ }
5729
+ }
5681
5730
  let tarball;
5682
5731
  try {
5683
- tarball = await buildTarball(ctx.cwd);
5732
+ tarball = await buildTarball(targetDir);
5684
5733
  } catch (e) {
5685
5734
  return errorResult(EXIT.UNEXPECTED, "provider", `could not package the project: ${e.message}`, "Ensure `tar` is available and the directory is readable.");
5686
5735
  }
@@ -6223,7 +6272,7 @@ var USAGE = [
6223
6272
  "usage: openpouch <command> [--json]",
6224
6273
  "",
6225
6274
  "commands:",
6226
- " deploy zero-config instant preview on openpouch's own infra (anonymous, or under your account if a key is set)",
6275
+ " deploy [dir] zero-config instant preview on openpouch's own infra \u2014 deploys ./<dir> (e.g. `deploy dist`) or the current folder (anonymous, or under your account if a key is set)",
6227
6276
  " signup create an openpouch account: --email <addr> or --github (gives you an API key)",
6228
6277
  " activate finish an email signup: --account <id> --token <token> (saves your API key)",
6229
6278
  " whoami show the account behind your API key: tier + current usage",
@@ -6297,7 +6346,7 @@ async function run(argv, ctx) {
6297
6346
  try {
6298
6347
  switch (command) {
6299
6348
  case "deploy":
6300
- return render(await instantCommand(ctx), json);
6349
+ return render(await instantCommand(ctx, parsed.positionals[1]), json);
6301
6350
  case "signup":
6302
6351
  return render(await signupCommand(ctx, { email: parsed.values.email, github: parsed.values.github === true }), json);
6303
6352
  case "activate":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openpouch",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "openpouch 🦘 — agent-native hosting, built for coding agents. Deploy any folder to a live URL in one command: npx openpouch deploy",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",