wirl 0.2.0 → 0.2.1
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/README.md +12 -9
- package/bin.js +99 -36
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,17 +3,16 @@
|
|
|
3
3
|
Deploy an internal app somewhere real, privately. One command puts the folder you are in on a URL that only your company can open.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
|
|
7
|
-
wirl
|
|
8
|
-
wirl init my-tool --server
|
|
6
|
+
npx wirl login # a link and a code; approve in the browser
|
|
7
|
+
npx wirl init my-tool --server
|
|
9
8
|
cd my-tool
|
|
10
|
-
wirl dev
|
|
11
|
-
wirl preview
|
|
12
|
-
wirl deploy
|
|
13
|
-
wirl share dana@company.com
|
|
9
|
+
npx wirl dev # runs it here, signed in as a stub user
|
|
10
|
+
npx wirl preview # a private address to look at it before it is live
|
|
11
|
+
npx wirl deploy # builds, scans, deploys; prints the URL
|
|
12
|
+
npx wirl share dana@company.com
|
|
14
13
|
```
|
|
15
14
|
|
|
16
|
-
|
|
15
|
+
`npx` fetches the CLI from npm the first time and needs no install beyond Node 22.13 or later; in a terminal npm first asks "Ok to proceed? (y)": answer y. Elsewhere in this README a command is written `wirl <command>`, for short: run it as `npx wirl <command>`, or run `npm install -g wirl` once and use plain `wirl`. If your npm registry does not carry wirl, or to run exactly the build app.wirl.dev serves, install from app.wirl.dev instead: `npm install -g https://app.wirl.dev/cli/wirl.tgz`, and run plain `wirl`.
|
|
17
16
|
|
|
18
17
|
Sign in with your work Google account and you are in your company's workspace. Anything a colleague deploys there is yours to open, and nobody outside the company can. A personal account gets a space of its own.
|
|
19
18
|
|
|
@@ -41,11 +40,15 @@ That opens the browser once to sign in. The token an agent gets can deploy, roll
|
|
|
41
40
|
## Everything else
|
|
42
41
|
|
|
43
42
|
```bash
|
|
44
|
-
wirl --help
|
|
43
|
+
npx wirl --help
|
|
45
44
|
```
|
|
46
45
|
|
|
47
46
|
Orgs, members, connections, transfer, move, versions, rollback, pull, previews, logs, schedules, run.
|
|
48
47
|
|
|
48
|
+
## Versions
|
|
49
|
+
|
|
50
|
+
The npm package and the control plane are versioned separately. `npx wirl` runs the latest CLI published to npm, and app.wirl.dev runs the build it was last deployed with, so either can be the newer. A CLI newer than the control plane says so where it can tell: `its control plane is older than this copy of wirl` (from `wirl migrations` and `wirl migrations adopt`), or `this control plane is older than sending wirl.json in the body, so it cannot take it` when a `wirl.json` is too large for a header. A CLI older than the control plane sends your whole `wirl.json` and says of a key it does not read that it `is not a key this version of wirl reads, so it is ignored`, though the control plane may read it; it checks `wirl.json` against its own rules before it uploads, so update the CLI (`npm install -g wirl@latest`, or `npx wirl@latest <command>`) when a deploy is refused on your machine for something the docs allow.
|
|
51
|
+
|
|
49
52
|
## Requirements
|
|
50
53
|
|
|
51
54
|
Node 22.13 or later. Credentials live in `~/.config/wirl/credentials.json` after `wirl login`, or in `WIRL_TOKEN` for CI and sandboxes.
|
package/bin.js
CHANGED
|
@@ -3972,20 +3972,21 @@ arrives: pass \`response.body\` straight on. There are no WebSockets. A front en
|
|
|
3972
3972
|
\`"static": "dist"\` serves that folder's files at their own paths (\`/\` is its \`index.html\`) and the module
|
|
3973
3973
|
answers everything else; add \`"spa": true\` for a client-side router. Nothing outside that folder is served.
|
|
3974
3974
|
|
|
3975
|
-
Preview before you deploy: \`wirl preview\`
|
|
3976
|
-
|
|
3977
|
-
\`wirl_preview_fetch\` tool) shows you what it serves.
|
|
3975
|
+
Preview before you deploy: \`wirl preview\` gives a folder its own address and database (at first only what
|
|
3976
|
+
\`migrations/\` makes). \`wirl fetch <path> --preview <name>\` or \`wirl_preview_fetch\` shows what it answers.
|
|
3978
3977
|
|
|
3979
3978
|
npm packages work: install them in the project and the deploy bundles them in. Nothing is installed on the
|
|
3980
3979
|
server: native code will not run. Node built-ins work (\`node:crypto\`, \`node:path\`, ...; \`Buffer\` and
|
|
3981
3980
|
\`process\` are global), but what \`node:fs\` writes is gone by the next request: use \`env.DB\`.
|
|
3982
3981
|
|
|
3983
3982
|
\`env\` holds \`DB\` and nothing else. \`env.DB\` is Cloudflare D1: prepared statements with \`.bind()\`, at most
|
|
3984
|
-
100 bound parameters a statement. \`exec()\` runs one statement per line
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3983
|
+
100 bound parameters a statement. \`exec()\` runs one statement per line, as does each file in \`migrations/\`,
|
|
3984
|
+
which wirl applies in order to a preview and before going live; a failing one fails the deploy. Never edit
|
|
3985
|
+
an applied file. Dropping or renaming a table or column needs \`"migrations": { "destructive": [...] }\`, its
|
|
3986
|
+
own live deploy and a person's yes. Declare a host under \`connections\`, then \`fetch\` the vendor's real URL:
|
|
3987
|
+
wirl attaches the key. An undeclared host is refused; a public API needing no key uses \`"kind": "none"\`.
|
|
3988
|
+
Declaring one deploys before anyone approves it; the deploy says per connection whether it is usable now or
|
|
3989
|
+
waiting on an admin. A vendor has 60 seconds to answer, whole, unless it streams: ask a model for a stream.
|
|
3989
3990
|
|
|
3990
3991
|
Six headers say who is asking: \`x-wirl-user-id\`, \`x-wirl-user-email\`, \`x-wirl-user-name\`,
|
|
3991
3992
|
\`x-wirl-role\`, \`x-wirl-app-id\`, \`x-wirl-org\`. Read the name with
|
|
@@ -3995,10 +3996,9 @@ Schedules POST to \`/__wirl/schedule/<name>\`; a run gets 5 minutes unless that
|
|
|
3995
3996
|
says otherwise, and at most 15. A \`public_paths\` path takes requests with no sign-in.
|
|
3996
3997
|
Work that outlives one request (waiting for a person, trying tomorrow, a long loop) is a flow: see Flows.
|
|
3997
3998
|
|
|
3998
|
-
A new app defaults to org-wide visibility when its org has a company email domain,
|
|
3999
|
-
|
|
4000
|
-
Sharing by email adds one named person
|
|
4001
|
-
sets the level. Public is only ever a person's click on the app's Access page.
|
|
3999
|
+
A new app defaults to org-wide visibility when its org has a company email domain, and to private
|
|
4000
|
+
otherwise, including one made with \`wirl orgs create\`; \`wirl visibility private|org\` sets the level.
|
|
4001
|
+
Sharing by email adds one named person. Public is only ever a person's click on the app's Access page.
|
|
4002
4002
|
|
|
4003
4003
|
Porting an existing project: route it through the \`fetch\` handler, replace ORM calls with
|
|
4004
4004
|
\`env.DB.prepare(...).bind(...)\`, turn timers into \`schedules\`, and move keys into \`connections\`. Keep the
|
|
@@ -4030,18 +4030,40 @@ early, repeated or late one gets 409, or 404 once the run ends; check \`x-wirl-u
|
|
|
4030
4030
|
}
|
|
4031
4031
|
});
|
|
4032
4032
|
|
|
4033
|
+
// packages/core/src/version.ts
|
|
4034
|
+
function wirlVersion() {
|
|
4035
|
+
return BUILT_VERSION ?? WORKSPACE_VERSION;
|
|
4036
|
+
}
|
|
4037
|
+
var WORKSPACE_VERSION, BUILT_VERSION;
|
|
4038
|
+
var init_version = __esm({
|
|
4039
|
+
"packages/core/src/version.ts"() {
|
|
4040
|
+
"use strict";
|
|
4041
|
+
WORKSPACE_VERSION = "0.0.0";
|
|
4042
|
+
BUILT_VERSION = true ? "0.2.1" : null;
|
|
4043
|
+
}
|
|
4044
|
+
});
|
|
4045
|
+
|
|
4033
4046
|
// packages/core/src/install.ts
|
|
4047
|
+
function fallbackWhen(controlPlane = "this control plane") {
|
|
4048
|
+
return `If your npm registry does not carry wirl, or to run exactly the build ${controlPlane} serves,`;
|
|
4049
|
+
}
|
|
4050
|
+
function namedControlPlane(controlOrigin) {
|
|
4051
|
+
return `the wirl control plane (${controlOrigin.replace(/^https?:\/\//, "").replace(/\/+$/, "")})`;
|
|
4052
|
+
}
|
|
4034
4053
|
function installFor(controlOrigin) {
|
|
4035
|
-
if (NPM_PUBLISHED) return { cli: "wirl", mcp: "@wirl/mcp" };
|
|
4036
4054
|
const origin = controlOrigin.replace(/\/+$/, "");
|
|
4037
|
-
|
|
4055
|
+
const tarball = { cli: `${origin}/cli/wirl.tgz`, mcp: `${origin}/cli/wirl-mcp.tgz` };
|
|
4056
|
+
if (NPM_PUBLISHED) return { cli: "wirl", mcp: "@wirl/mcp", tarball };
|
|
4057
|
+
return { ...tarball, tarball };
|
|
4038
4058
|
}
|
|
4039
|
-
var NPM_PUBLISHED, WIRL_BIN;
|
|
4059
|
+
var NPM_PUBLISHED, WIRL_BIN, FALLBACK_WHEN, hasFallback;
|
|
4040
4060
|
var init_install = __esm({
|
|
4041
4061
|
"packages/core/src/install.ts"() {
|
|
4042
4062
|
"use strict";
|
|
4043
|
-
NPM_PUBLISHED =
|
|
4063
|
+
NPM_PUBLISHED = true;
|
|
4044
4064
|
WIRL_BIN = NPM_PUBLISHED ? "npx wirl" : "wirl";
|
|
4065
|
+
FALLBACK_WHEN = fallbackWhen();
|
|
4066
|
+
hasFallback = (install) => install.cli !== install.tarball.cli;
|
|
4045
4067
|
}
|
|
4046
4068
|
});
|
|
4047
4069
|
|
|
@@ -4322,6 +4344,7 @@ var init_src = __esm({
|
|
|
4322
4344
|
init_retention();
|
|
4323
4345
|
init_identity();
|
|
4324
4346
|
init_contract();
|
|
4347
|
+
init_version();
|
|
4325
4348
|
init_install();
|
|
4326
4349
|
init_porting();
|
|
4327
4350
|
init_supplied_bundle();
|
|
@@ -6460,7 +6483,7 @@ async function loadCredentials(env = process.env) {
|
|
|
6460
6483
|
}
|
|
6461
6484
|
} catch {
|
|
6462
6485
|
}
|
|
6463
|
-
throw new CliError("Not signed in.",
|
|
6486
|
+
throw new CliError("Not signed in.", `Run \`${WIRL_BIN} login\` first, or set WIRL_TOKEN.`, { code: "not_signed_in" });
|
|
6464
6487
|
}
|
|
6465
6488
|
async function saveCredentials(creds, env = process.env) {
|
|
6466
6489
|
const path = credentialsPath(env);
|
|
@@ -6521,11 +6544,11 @@ async function pollDeviceLogin(apiOrigin, login, options = {}) {
|
|
|
6521
6544
|
continue;
|
|
6522
6545
|
}
|
|
6523
6546
|
case "expired_token":
|
|
6524
|
-
throw new CliError("The sign-in code expired before it was approved.",
|
|
6547
|
+
throw new CliError("The sign-in code expired before it was approved.", `Run \`${WIRL_BIN} login\` again.`);
|
|
6525
6548
|
default:
|
|
6526
6549
|
throw new CliError(
|
|
6527
6550
|
typeof body["error"] === "string" ? `Sign-in failed: ${body["error"]}.` : `Sign-in failed (${response.status}).`,
|
|
6528
|
-
|
|
6551
|
+
`Run \`${WIRL_BIN} login\` again.`
|
|
6529
6552
|
);
|
|
6530
6553
|
}
|
|
6531
6554
|
}
|
|
@@ -6636,6 +6659,10 @@ var SERVER_TEMPLATE = [
|
|
|
6636
6659
|
" *",
|
|
6637
6660
|
" * Everything real happens here. The page is HTML this module returns. The",
|
|
6638
6661
|
" * data lives in env.DB, a SQLite database that belongs to this app alone.",
|
|
6662
|
+
" * Its tables are in migrations/0001_init.sql: wirl applies each new file in",
|
|
6663
|
+
" * migrations/ before a version goes live, to a preview's database when the",
|
|
6664
|
+
" * preview is put up, and to the local one when wirl dev starts. Put the",
|
|
6665
|
+
" * next change in migrations/0002_<what>.sql rather than editing that one.",
|
|
6639
6666
|
" * The person making the request is in headers wirl sets after verifying",
|
|
6640
6667
|
" * them, which the browser cannot forge. Any rule that matters, such as",
|
|
6641
6668
|
' * "you only see your own notes", is a line of code in this file: a check',
|
|
@@ -6647,18 +6674,8 @@ var SERVER_TEMPLATE = [
|
|
|
6647
6674
|
" * live in .wirl/keys.json beside this file, which git ignores.",
|
|
6648
6675
|
" */",
|
|
6649
6676
|
"",
|
|
6650
|
-
"// One line: exec runs one statement per line, as D1 does.",
|
|
6651
|
-
"const SCHEMA =",
|
|
6652
|
-
' "create table if not exists notes (" +',
|
|
6653
|
-
' " id integer primary key," +',
|
|
6654
|
-
' " user_id text not null," +',
|
|
6655
|
-
' " body text not null," +',
|
|
6656
|
-
` " created_at text not null default (datetime('now'))" +`,
|
|
6657
|
-
' ")";',
|
|
6658
|
-
"",
|
|
6659
6677
|
"export default {",
|
|
6660
6678
|
" async fetch(request, env) {",
|
|
6661
|
-
" await env.DB.exec(SCHEMA);",
|
|
6662
6679
|
" const url = new URL(request.url);",
|
|
6663
6680
|
"",
|
|
6664
6681
|
` // One line per request. Read it with ${WIRL_BIN} logs.`,
|
|
@@ -6741,7 +6758,41 @@ var SERVER_TEMPLATE = [
|
|
|
6741
6758
|
"}",
|
|
6742
6759
|
""
|
|
6743
6760
|
].join("\n");
|
|
6761
|
+
var INIT_MIGRATION = [
|
|
6762
|
+
"-- This app's tables. wirl applies each new file here, in order, before a version goes live.",
|
|
6763
|
+
"-- Make the next change in 0002_<what>.sql; never edit a file that has been deployed.",
|
|
6764
|
+
"create table notes (id integer primary key, user_id text not null, body text not null, created_at text not null default (datetime('now')))",
|
|
6765
|
+
""
|
|
6766
|
+
].join("\n");
|
|
6767
|
+
var INIT_MIGRATION_PATH = `${MIGRATIONS_DIR}/${ADOPTED_FILE}`;
|
|
6768
|
+
async function refuseInitMigrations(dir) {
|
|
6769
|
+
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
|
6770
|
+
if (entry.name.toLowerCase() !== MIGRATIONS_DIR) continue;
|
|
6771
|
+
if (entry.isSymbolicLink()) {
|
|
6772
|
+
throw new CliError(`${entry.name}/ is a symbolic link, and wirl writes into no links: make migrations/ a real folder, then run wirl init --server again.`);
|
|
6773
|
+
}
|
|
6774
|
+
if (entry.name !== MIGRATIONS_DIR) {
|
|
6775
|
+
throw new CliError(`${entry.name}/ is spelled differently from migrations/: wirl reads migrations/ in lowercase only. Rename it, then run wirl init --server again.`);
|
|
6776
|
+
}
|
|
6777
|
+
if (!entry.isDirectory()) {
|
|
6778
|
+
throw new CliError(
|
|
6779
|
+
`migrations is a file, and wirl init --server writes ${INIT_MIGRATION_PATH} into a folder of that name. Move it aside, then run wirl init --server again.`
|
|
6780
|
+
);
|
|
6781
|
+
}
|
|
6782
|
+
const names = await readdir(join5(dir, entry.name));
|
|
6783
|
+
if (names.some((name) => name.toLowerCase().endsWith(".sql"))) {
|
|
6784
|
+
throw new CliError(`migrations/ already holds .sql files; wirl init --server writes ${INIT_MIGRATION_PATH} into a folder with none.`);
|
|
6785
|
+
}
|
|
6786
|
+
}
|
|
6787
|
+
}
|
|
6744
6788
|
var SKILL_PATH = join5(".claude", "skills", "wirl", "SKILL.md");
|
|
6789
|
+
var SKILL_ORIGIN = "https://app.wirl.dev";
|
|
6790
|
+
var SKILL_INSTALL = installFor(SKILL_ORIGIN);
|
|
6791
|
+
var SKILL_RUN = NPM_PUBLISHED ? `Run the CLI as \`${WIRL_BIN} <command>\`, which needs no install beyond Node 22.13 or later (in a terminal npm asks "Ok to proceed?" the first time: answer y); this file writes a command as \`wirl <command>\` for short anywhere but the list below.
|
|
6792
|
+
|
|
6793
|
+
` : "";
|
|
6794
|
+
var SKILL_FALLBACK = hasFallback(SKILL_INSTALL) ? `- ${fallbackWhen(namedControlPlane(SKILL_ORIGIN))} install the CLI from the control plane instead with \`npm install -g ${SKILL_INSTALL.tarball.cli}\`, run plain \`wirl\` where this file says \`${WIRL_BIN}\`, and name \`${SKILL_INSTALL.tarball.mcp}\` in place of \`${SKILL_INSTALL.mcp}\`.
|
|
6795
|
+
` : "";
|
|
6745
6796
|
var SKILL_MARKDOWN = `---
|
|
6746
6797
|
name: wirl
|
|
6747
6798
|
description: Deploy and share the internal app in this project with wirl (${WIRL_BIN}). Use when the user wants a tool, dashboard or prototype put somewhere colleagues can open it, shared with a named person, run locally the way it will run deployed, or connected to a third-party API; or wants work that outlives one request, such as waiting for a person to approve something or trying again tomorrow.
|
|
@@ -6758,20 +6809,23 @@ Before you build, fetch https://docs.wirl.dev/llms-full.txt: every page of the d
|
|
|
6758
6809
|
|
|
6759
6810
|
## Commands
|
|
6760
6811
|
|
|
6761
|
-
- Connect first, if the agent supports MCP: \`claude mcp add --transport http wirl https://app.wirl.dev/mcp\` opens the browser once to sign in. No HTTP MCP support? \`claude mcp add wirl -- npx -y ${
|
|
6812
|
+
${SKILL_RUN}- Connect first, if the agent supports MCP: \`claude mcp add --transport http wirl https://app.wirl.dev/mcp\` opens the browser once to sign in, and needs no install. No HTTP MCP support? \`claude mcp add wirl -- npx -y ${SKILL_INSTALL.mcp}\` does the same, running locally. Neither available: \`${WIRL_BIN} login\` prints a link and a code; the person approves in the browser. Do this once, when any command says you are not signed in.
|
|
6762
6813
|
- \`${WIRL_BIN} deploy\` \u2014 deploys this directory. Prints the URL and who can open it.
|
|
6763
6814
|
- \`${WIRL_BIN} share someone@company.com\` \u2014 that person can open it. Add \`--role editor\` so they can deploy too.
|
|
6764
6815
|
- \`${WIRL_BIN} dev\` \u2014 runs the app here as it will run deployed, signed in as a stub user.
|
|
6765
6816
|
- \`${WIRL_BIN} pull <app>\` \u2014 fetches a deployed app's source to keep building.
|
|
6766
6817
|
- \`${WIRL_BIN} logs\` \u2014 what the deployed app printed, per request, newest first; \`--errors\` for failures only, \`--follow\` to keep watching, \`--cursor <n>\` for the page before a "More before this" line. Read this before changing code when something misbehaves.
|
|
6818
|
+
- \`${WIRL_BIN} migrations\` \u2014 what this app's database has applied, read only.
|
|
6819
|
+
- \`${WIRL_BIN} migrations adopt\` \u2014 once, for an app whose tables were made in code: writes \`migrations/0001_init.sql\` from the live database's shape.
|
|
6767
6820
|
- \`${WIRL_BIN} --help\` \u2014 everything else: orgs, members, connections, move, transfer, rollback, flows and runs.
|
|
6768
|
-
|
|
6821
|
+
${SKILL_FALLBACK}
|
|
6769
6822
|
## Rules
|
|
6770
6823
|
|
|
6771
6824
|
1. **Never ask for or paste an API key.** Declare the connection in \`wirl.json\` and deploy. Needs no key at all (a public API)? Declare it with "kind": "none" and its hosts. If the key is missing, the deploy prints a link to the app's keys page; show the person that link. Locally, keys live in \`.wirl/keys.json\`, which git ignores. To try a vendor call locally without the key, map its connection to a server of your own in \`.wirl/mocks.json\`, like \`{ "anthropic": "http://127.0.0.1:4010" }\`: \`wirl dev\` then sends the calls to that connection's hosts there instead (\`wirl dev --help\` has the rest).
|
|
6772
6825
|
2. **Call third-party APIs by their real URL** (\`fetch("https://api.hubspot.com/...")\`). wirl attaches the key on the way out, deployed and under \`wirl dev\` alike.
|
|
6773
6826
|
3. If a command names several organisations and asks which, ask the person; pass \`--org\` or put \`"org"\` in \`wirl.json\`.
|
|
6774
6827
|
4. To keep a file out of the deploy, list it in a \`.wirlignore\` at the project root (gitignore-style lines; no \`!\` negation).
|
|
6828
|
+
5. **A file that can lose data waits for a person.** A deploy that would apply a destructive file (one wirl.json's \`"destructive"\` list must name) to the live database stops and names the page where a person approves each one. Show the person those pages, and deploy again once they say they approved. If \`${WIRL_BIN} deploy\` asks you to type a file's name, stop it and show the page instead, or deploy with \`--json\`, which never asks. Never type the name yourself or send the confirmation: that is the person's, and no flag skips it.
|
|
6775
6829
|
|
|
6776
6830
|
## wirl.json
|
|
6777
6831
|
|
|
@@ -6822,8 +6876,12 @@ async function scaffold(dir, options) {
|
|
|
6822
6876
|
} catch (error) {
|
|
6823
6877
|
if (error instanceof CliError) throw error;
|
|
6824
6878
|
}
|
|
6879
|
+
await refuseInitMigrations(dir);
|
|
6825
6880
|
await writeFile2(target, SERVER_TEMPLATE, "utf8");
|
|
6826
6881
|
written.push("server.mjs");
|
|
6882
|
+
await mkdir3(join5(dir, MIGRATIONS_DIR), { recursive: true });
|
|
6883
|
+
await writeFile2(join5(dir, MIGRATIONS_DIR, ADOPTED_FILE), INIT_MIGRATION, { encoding: "utf8", flag: "wx" });
|
|
6884
|
+
written.push(INIT_MIGRATION_PATH);
|
|
6827
6885
|
const pkg = join5(dir, "package.json");
|
|
6828
6886
|
if (await readFile3(pkg, "utf8").catch(() => null) === null) {
|
|
6829
6887
|
await writeFile2(pkg, `${JSON.stringify({ name: options.app, private: true, type: "module" }, null, 2)}
|
|
@@ -7113,7 +7171,7 @@ async function callApi(creds, path, options = {}) {
|
|
|
7113
7171
|
throw new CliError(body.message, void 0, refusal);
|
|
7114
7172
|
}
|
|
7115
7173
|
if (response.status === 401 || response.status === 403) {
|
|
7116
|
-
throw new CliError("Not signed in, or this token cannot do that.",
|
|
7174
|
+
throw new CliError("Not signed in, or this token cannot do that.", `Try \`${WIRL_BIN} login\`.`, refusal);
|
|
7117
7175
|
}
|
|
7118
7176
|
if (response.status === 404) {
|
|
7119
7177
|
if (body.error !== void 0 && body.error !== "not_found" && body.message) throw new CliError(body.message, void 0, refusal);
|
|
@@ -7151,7 +7209,7 @@ async function callApiBytes(creds, path, options = {}) {
|
|
|
7151
7209
|
body = {};
|
|
7152
7210
|
}
|
|
7153
7211
|
if (response.status === 401 || response.status === 403) {
|
|
7154
|
-
throw new CliError("Not signed in, or this token cannot do that.",
|
|
7212
|
+
throw new CliError("Not signed in, or this token cannot do that.", `Try \`${WIRL_BIN} login\`.`);
|
|
7155
7213
|
}
|
|
7156
7214
|
if (response.status === 404) throw new CliError("Not found, or you do not have access to it.");
|
|
7157
7215
|
throw new CliError(body.message ?? body.error ?? `Request failed (${response.status}).`);
|
|
@@ -7551,7 +7609,7 @@ function deletedAppMessage(org, app, done) {
|
|
|
7551
7609
|
return `Deleted ${org}/${app}. Its address has stopped answering${runs}. Its data is kept until ${done.dataKeptUntil.slice(0, 10)}; until then wirl apps restore ${app} --org ${org} brings it back (one that was public comes back only from the Recently deleted list on the dashboard). Its name stays taken.`;
|
|
7552
7610
|
}
|
|
7553
7611
|
function deleteNeedsFullScopeMessage(apiOrigin, org, app) {
|
|
7554
|
-
return `This sign-in can deploy, but not delete an app: deleting one needs a full-scope sign-in. Sign in with
|
|
7612
|
+
return `This sign-in can deploy, but not delete an app: deleting one needs a full-scope sign-in. Sign in with ${WIRL_BIN} login (with WIRL_TOKEN unset), or delete it on its Settings page: ${apiOrigin}/apps/${seg(org)}/${seg(app)}/settings`;
|
|
7555
7613
|
}
|
|
7556
7614
|
var VISIBILITY_LEVELS = ["private", "org", "public"];
|
|
7557
7615
|
function describeVisibility(level, org) {
|
|
@@ -8642,7 +8700,7 @@ var ADOPT_SENTENCES2 = {
|
|
|
8642
8700
|
*/
|
|
8643
8701
|
cannotTell: (what) => `wirl did not get the adopt route's answer (${what}), so it cannot tell whether the live database recorded migrations/${ADOPTED_FILE}, and nothing was written here. Run wirl migrations adopt again: while an adopted ${ADOPTED_FILE} is all the live database records, adopting again replaces that record and writes the file.`,
|
|
8644
8702
|
/** After the call, so the live database records the file; a CLI that reads files otherwise than the control plane says this every time. */
|
|
8645
|
-
notAsRecorded: (install) => `The live database now records migrations/${ADOPTED_FILE} as adopted, but the file wirl sent does not match its own record of it, so nothing was written here. Run wirl migrations adopt again: while an adopted ${ADOPTED_FILE} is all the live database records, adopting again replaces that record and writes the file. If it says this again, this copy of wirl reads migration files differently from the control plane: update it (npm install -g ${install}).`,
|
|
8703
|
+
notAsRecorded: (install) => `The live database now records migrations/${ADOPTED_FILE} as adopted, but the file wirl sent does not match its own record of it, so nothing was written here. Run wirl migrations adopt again: while an adopted ${ADOPTED_FILE} is all the live database records, adopting again replaces that record and writes the file. If it says this again, this copy of wirl reads migration files differently from the control plane: update it (npm install -g ${install.cli}${hasFallback(install) ? `, or, to match this control plane exactly, npm install -g ${install.tarball.cli}` : ""}).`,
|
|
8646
8704
|
unwritten: (code) => `The live database now records migrations/${ADOPTED_FILE} as adopted, but wirl could not write that file here (${code}). Make migrations/ a folder you can write that holds no .sql file, then run wirl migrations adopt again: while an adopted ${ADOPTED_FILE} is all the live database records, adopting again replaces that record and writes the file.`,
|
|
8647
8705
|
nothingWritten: "Nothing was written to migrations/.",
|
|
8648
8706
|
/**
|
|
@@ -8833,7 +8891,7 @@ async function adoptIntoProject(creds, options) {
|
|
|
8833
8891
|
const read = await readMigrations([{ name: ADOPTED_FILE, text: answer.file.text }], []);
|
|
8834
8892
|
const [file, ...others] = read.ok ? read.files : [];
|
|
8835
8893
|
if (file === void 0 || others.length > 0 || file.sha256 !== answer.file.sha256 || file.statements.length !== answer.file.statements) {
|
|
8836
|
-
throw new CliError(ADOPT_SENTENCES2.notAsRecorded(installFor(creds.apiOrigin)
|
|
8894
|
+
throw new CliError(ADOPT_SENTENCES2.notAsRecorded(installFor(creds.apiOrigin)));
|
|
8837
8895
|
}
|
|
8838
8896
|
const path = await writeAdopted(projectDir, answer.file.text);
|
|
8839
8897
|
const { recordLocalAdoption: recordLocalAdoption2 } = await Promise.resolve().then(() => (init_migrations_local(), migrations_local_exports));
|
|
@@ -9917,6 +9975,7 @@ Options
|
|
|
9917
9975
|
--app <slug> App. Defaults to wirl.json or the directory name.
|
|
9918
9976
|
--no-create Fail instead of creating an app that does not exist yet.
|
|
9919
9977
|
--json Machine-readable output.
|
|
9978
|
+
--version Print the version of wirl you are running, as the first word: wirl --version.
|
|
9920
9979
|
|
|
9921
9980
|
wirl <command> --help for a command's flags and an example.
|
|
9922
9981
|
`;
|
|
@@ -10411,6 +10470,10 @@ async function resolveTarget(creds, args2, dir) {
|
|
|
10411
10470
|
var args = parseArgs(process.argv.slice(2).map((token) => token === "-h" ? "--help" : token));
|
|
10412
10471
|
async function main() {
|
|
10413
10472
|
const json2 = args.flags.has("json");
|
|
10473
|
+
if (process.argv[2] === "--version" || process.argv[2] === "-v") {
|
|
10474
|
+
out(wirlVersion());
|
|
10475
|
+
return 0;
|
|
10476
|
+
}
|
|
10414
10477
|
if (args.command === "help") {
|
|
10415
10478
|
if (!args.flags.has("help")) {
|
|
10416
10479
|
refuseExtraPositionals("help", args.positional, 1, "one command", { hint: "A command's help lists its subcommands: wirl help <command>." });
|