@workser/cli 0.6.21 → 0.6.23

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/dist/index.js CHANGED
@@ -4569,8 +4569,39 @@ turn.
4569
4569
 
4570
4570
  Types: \`input\` (default, free text), \`choice\` (with \`--option\`), \`approval\`
4571
4571
  (permission), \`confirmation\` (check an assumption), \`information\` (FYI, no answer
4572
- needed). It times out (default 10 min) rather than hanging forever; if it does, carry
4573
- on and state clearly what you assumed.
4572
+ needed), \`create_app\` (below). It times out (default 10 min) rather than hanging
4573
+ forever; if it does, carry on and state clearly what you assumed.
4574
+
4575
+ ## Asking for an app that does not exist yet
4576
+
4577
+ The project needs a kind of app it does not have \u2014 a backend for the phone app, a
4578
+ phone version of the website. **You cannot create one yourself**, by design: apps
4579
+ are real infrastructure on the owner's account. Ask, and their click creates it.
4580
+ The answer gives you the new app's id.
4581
+
4582
+ \`\`\`
4583
+ workser ask "The phone app needs an API to hold its data. Add one?" \\
4584
+ --type create_app --app-type api --app-name "Lattice Drive API"
4585
+ \`\`\`
4586
+
4587
+ \`--app-type\` is required and must be one of:
4588
+
4589
+ | | |
4590
+ |---|---|
4591
+ | \`web\` | Next.js site on Workser hosting |
4592
+ | \`mobile\` | Expo / React Native phone app |
4593
+ | \`api\` | backend service \u2014 \`api-hono\` (TypeScript) or \`api-python\` (FastAPI) |
4594
+
4595
+ **Name the kind you actually mean.** Asking for \`web\` because you are unsure is
4596
+ how a phone app gets created as a website: the card shows the owner the kind
4597
+ you named, they approve THAT, and the wrong app is provisioned under your own
4598
+ sentence asking for the right one. An unknown kind is refused with the list
4599
+ above rather than guessed at \u2014 read it and ask again.
4600
+
4601
+ **There is no desktop app type.** Workser builds websites, phone apps and
4602
+ backend services. If the owner asks for a Mac or Windows app, say plainly that
4603
+ Workser cannot make one yet \u2014 do not offer a web app dressed as a window, and
4604
+ do not build a "desktop-style" page instead. That is not what they asked for.
4574
4605
 
4575
4606
  **Never ask for a secret value this way** \u2014 the answer is stored and displayed. Ask
4576
4607
  *where* a key should go, then have the user set it (\`workser env set\` writes it
@@ -5156,6 +5187,35 @@ connect to it** \u2014 useful as a snapshot, useless as somewhere to run tests.
5156
5187
  is this database costing me while nothing is happening".
5157
5188
  - **Most apps never need the storage or functions half.** If the user just wants
5158
5189
  to store uploads, the default bucket in \`reference/storage.md\` is the answer.
5190
+
5191
+ ## Sign-in for the app you are building
5192
+
5193
+ **WEB APPS ONLY.** Everything in this section is about the \`web\` app type. Neon's
5194
+ managed auth is consumed through a Next.js server route (\`/api/auth/*\`), so it
5195
+ does not reach a mobile app, and it does not reach a desktop app either \u2014 that
5196
+ type is a static export and cannot serve a route at all. For those two, auth
5197
+ belongs in the project's API service, self-hosted, where Google needs the owner's
5198
+ own credentials like anywhere else. Do not tell a phone-app owner that Google
5199
+ sign-in is free.
5200
+
5201
+ **On a web app with Neon-managed auth, Google sign-in is already on.** Neon runs
5202
+ it on its own shared OAuth credentials \u2014 there is no client ID to create, no
5203
+ secret to set, no redirect URI to register. If the owner asks for "login with
5204
+ Google", build the button; do not send them to the Google Cloud Console.
5205
+
5206
+ Check which mode the app is on before answering: \`workser env get AUTH_MODE\`.
5207
+ \`neon_managed\` means the above. Anything else (or absent) is self-hosted, where
5208
+ Google needs the owner's own \`GOOGLE_CLIENT_ID\`/\`GOOGLE_CLIENT_SECRET\` and a
5209
+ redirect URI per domain.
5210
+
5211
+ Two caveats worth saying out loud rather than letting the owner find later:
5212
+
5213
+ - **The consent screen says Neon, not their brand**, and the quota is shared.
5214
+ Correct for a prototype, wrong once real users sign in. Moving to their own
5215
+ Google client is done in the desktop app under **Cloud -> Auth -> Sign-in
5216
+ providers**, which also shows the exact redirect URI to register.
5217
+ - **GitHub and Vercel have no shared credentials.** Those stay off until the
5218
+ owner installs their own client in that same place.
5159
5219
  `
5160
5220
  },
5161
5221
  {
@@ -9133,6 +9193,19 @@ var TYPES = [
9133
9193
  */
9134
9194
  "create_app"
9135
9195
  ];
9196
+ var APP_TYPES = [
9197
+ "web",
9198
+ "mobile",
9199
+ // NOT OFFERED AT FIRST LAUNCH (2026-09-05) — `desktop` is `creatable: false`
9200
+ // in core-api, so the card would error on the owner's click. Re-enable
9201
+ // together with that flag; grep the marker.
9202
+ // "desktop",
9203
+ // `api` is the shorthand; the two concrete spellings pick the runtime and
9204
+ // are accepted at this boundary for an agent that knows which one it wants.
9205
+ "api",
9206
+ "api-hono",
9207
+ "api-python"
9208
+ ];
9136
9209
  function registerAsk(program3) {
9137
9210
  program3.command("ask <message>").description("Ask the user a question and wait for their answer").option(
9138
9211
  "-t, --type <type>",
@@ -9145,7 +9218,7 @@ function registerAsk(program3) {
9145
9218
  []
9146
9219
  ).option(
9147
9220
  "--app-type <type>",
9148
- "create_app only: web | mobile | api | worker | cron | python"
9221
+ `create_app only: ${APP_TYPES.join(" | ")}`
9149
9222
  ).option(
9150
9223
  "--app-name <name>",
9151
9224
  "create_app only: what the app should be called"
@@ -9177,7 +9250,13 @@ function registerAsk(program3) {
9177
9250
  }
9178
9251
  if (type === "create_app" && !opts.appType) {
9179
9252
  throw new WorkserError(
9180
- "Say which kind of app: `--app-type mobile`.",
9253
+ `Say which kind of app: \`--app-type desktop\`. One of: ${APP_TYPES.join(", ")}.`,
9254
+ { code: "bad_request" }
9255
+ );
9256
+ }
9257
+ if (type === "create_app" && opts.appType && !APP_TYPES.includes(opts.appType)) {
9258
+ throw new WorkserError(
9259
+ `Unknown --app-type "${opts.appType}". Use one of: ${APP_TYPES.join(", ")}.`,
9181
9260
  { code: "bad_request" }
9182
9261
  );
9183
9262
  }
@@ -12274,7 +12353,7 @@ function colour(d) {
12274
12353
 
12275
12354
  // src/index.ts
12276
12355
  var pkg = {
12277
- version: true ? "0.6.21" : "0.0.0-dev"
12356
+ version: true ? "0.6.23" : "0.0.0-dev"
12278
12357
  };
12279
12358
  var program2 = new Command();
12280
12359
  program2.name("workser").description(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workser/cli",
3
- "version": "0.6.21",
3
+ "version": "0.6.23",
4
4
  "description": "Workser CLI — give your local AI agent native DevOps & infrastructure on Workser. The agent runs `workser …` to provision, deploy, and manage real apps.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -95,8 +95,39 @@ turn.
95
95
 
96
96
  Types: `input` (default, free text), `choice` (with `--option`), `approval`
97
97
  (permission), `confirmation` (check an assumption), `information` (FYI, no answer
98
- needed). It times out (default 10 min) rather than hanging forever; if it does, carry
99
- on and state clearly what you assumed.
98
+ needed), `create_app` (below). It times out (default 10 min) rather than hanging
99
+ forever; if it does, carry on and state clearly what you assumed.
100
+
101
+ ## Asking for an app that does not exist yet
102
+
103
+ The project needs a kind of app it does not have — a backend for the phone app, a
104
+ phone version of the website. **You cannot create one yourself**, by design: apps
105
+ are real infrastructure on the owner's account. Ask, and their click creates it.
106
+ The answer gives you the new app's id.
107
+
108
+ ```
109
+ workser ask "The phone app needs an API to hold its data. Add one?" \
110
+ --type create_app --app-type api --app-name "Lattice Drive API"
111
+ ```
112
+
113
+ `--app-type` is required and must be one of:
114
+
115
+ | | |
116
+ |---|---|
117
+ | `web` | Next.js site on Workser hosting |
118
+ | `mobile` | Expo / React Native phone app |
119
+ | `api` | backend service — `api-hono` (TypeScript) or `api-python` (FastAPI) |
120
+
121
+ **Name the kind you actually mean.** Asking for `web` because you are unsure is
122
+ how a phone app gets created as a website: the card shows the owner the kind
123
+ you named, they approve THAT, and the wrong app is provisioned under your own
124
+ sentence asking for the right one. An unknown kind is refused with the list
125
+ above rather than guessed at — read it and ask again.
126
+
127
+ **There is no desktop app type.** Workser builds websites, phone apps and
128
+ backend services. If the owner asks for a Mac or Windows app, say plainly that
129
+ Workser cannot make one yet — do not offer a web app dressed as a window, and
130
+ do not build a "desktop-style" page instead. That is not what they asked for.
100
131
 
101
132
  **Never ask for a secret value this way** — the answer is stored and displayed. Ask
102
133
  *where* a key should go, then have the user set it (`workser env set` writes it
@@ -0,0 +1,98 @@
1
+ ---
2
+ topic: desktop
3
+ title: Desktop apps — one codebase, a window, and an installer
4
+ summary: How a Workser desktop app is built, what breaks only after it is installed, and how to produce and publish a signed installer.
5
+ commands: [project, env, deploy]
6
+ ---
7
+
8
+ # Desktop apps
9
+
10
+ A Workser desktop app is **one Next.js codebase with two ways to run**: a normal
11
+ web build for development and preview, and an Electron shell that loads the
12
+ static export from disk and gets packaged into an installer.
13
+
14
+ There is no deploy. A desktop app has no Vercel project and no URL — its
15
+ preview is the app running locally, and its deliverable is a file someone
16
+ double-clicks.
17
+
18
+ ## The rule that breaks everything, and only after install
19
+
20
+ **The app must stay statically exportable.** A packaged Electron app loads from
21
+ `file://` and has **no Node server**. So none of this exists once it is
22
+ installed:
23
+
24
+ - server components that fetch at request time
25
+ - server actions (`"use server"`)
26
+ - route handlers under `app/api/*`
27
+ - `next/image` optimisation
28
+
29
+ Every one of them works in `npm run dev` **and** in the local preview, and fails
30
+ only on the owner's machine. That is the one direction of error nobody catches,
31
+ so `next.config.mjs` keeps `output: "export"` to make the build fail loudly
32
+ instead. Do not remove it to "fix" an import.
33
+
34
+ Need a server? Use the project's api app — `workser project apps` to find it,
35
+ then read its URL and set `NEXT_PUBLIC_API_URL`. See `workser help apps`.
36
+
37
+ ## Secrets: this is a public client
38
+
39
+ Anyone who installs the app can read every string inside it. So:
40
+
41
+ - `NEXT_PUBLIC_*` only, and nothing behind that prefix is private
42
+ - never a database URL, an API key, or a `wsgw_` gateway key
43
+ - anything privileged lives in the api app and is reached over HTTP
44
+
45
+ The manifest deliberately does not declare `AI_GATEWAY_API_KEY` for this type,
46
+ which is what stops Workser seeding one. Do not add it.
47
+
48
+ ## The two processes
49
+
50
+ | File | Runs where | May do |
51
+ | --- | --- | --- |
52
+ | `electron/main.js` | Node, on the machine | files, tray, windows, updates |
53
+ | `app/**` | the renderer, sandboxed | ordinary web code, no Node |
54
+ | `electron/preload.js` | the bridge | expose **named functions** only |
55
+
56
+ `contextIsolation: true` and `nodeIntegration: false` are not defaults to tidy
57
+ up — turning either off hands full filesystem and process access to page code.
58
+ Add a capability by exporting one named function from preload, never by handing
59
+ the renderer `ipcRenderer`.
60
+
61
+ ## Building and shipping
62
+
63
+ ```bash
64
+ npm run dev # browser only, fastest loop
65
+ npm run dev:desktop # the same app inside a real Electron window
66
+ npm run build # static export -> out/
67
+ npm run dist:desktop # the installer -> release/
68
+ ```
69
+
70
+ In the app, **Publish → Your installer** does the same thing with a target
71
+ picker, progress, and a download link at the end.
72
+
73
+ ## Signing — the owner's certificate, never Workser's
74
+
75
+ An unsigned build runs perfectly on the machine that made it and tells every
76
+ other machine the app is damaged (macOS) or trips SmartScreen (Windows). That
77
+ is the failure an owner cannot diagnose, so say it before they send the file.
78
+
79
+ `electron-builder` reads these from the environment; set them with
80
+ `workser env set --app <id>`:
81
+
82
+ | Platform | Keys |
83
+ | --- | --- |
84
+ | macOS | `CSC_LINK`, `CSC_KEY_PASSWORD` |
85
+ | macOS notarize | `APPLE_ID`, `APPLE_APP_SPECIFIC_PASSWORD`, `APPLE_TEAM_ID` |
86
+ | Windows | `WIN_CSC_LINK`, `WIN_CSC_KEY_PASSWORD` |
87
+
88
+ A Mac app can only be built on a Mac — `codesign` and `hdiutil` are Apple's and
89
+ have no substitute.
90
+
91
+ ## Auto-update
92
+
93
+ Publishing uploads the installer **and** the update metadata
94
+ (`latest-mac.yml` / `latest.yml`) beside it. `electron-updater` reads only that
95
+ metadata, so an installer published alone gives you a download that works once
96
+ and an app that can never update itself — and nobody notices until release two.
97
+ Keep `publish.url` in `electron-builder.yml` pointed at the prefix the publish
98
+ step reports back.
@@ -74,3 +74,32 @@ connect to it** — useful as a snapshot, useless as somewhere to run tests.
74
74
  is this database costing me while nothing is happening".
75
75
  - **Most apps never need the storage or functions half.** If the user just wants
76
76
  to store uploads, the default bucket in `reference/storage.md` is the answer.
77
+
78
+ ## Sign-in for the app you are building
79
+
80
+ **WEB APPS ONLY.** Everything in this section is about the `web` app type. Neon's
81
+ managed auth is consumed through a Next.js server route (`/api/auth/*`), so it
82
+ does not reach a mobile app, and it does not reach a desktop app either — that
83
+ type is a static export and cannot serve a route at all. For those two, auth
84
+ belongs in the project's API service, self-hosted, where Google needs the owner's
85
+ own credentials like anywhere else. Do not tell a phone-app owner that Google
86
+ sign-in is free.
87
+
88
+ **On a web app with Neon-managed auth, Google sign-in is already on.** Neon runs
89
+ it on its own shared OAuth credentials — there is no client ID to create, no
90
+ secret to set, no redirect URI to register. If the owner asks for "login with
91
+ Google", build the button; do not send them to the Google Cloud Console.
92
+
93
+ Check which mode the app is on before answering: `workser env get AUTH_MODE`.
94
+ `neon_managed` means the above. Anything else (or absent) is self-hosted, where
95
+ Google needs the owner's own `GOOGLE_CLIENT_ID`/`GOOGLE_CLIENT_SECRET` and a
96
+ redirect URI per domain.
97
+
98
+ Two caveats worth saying out loud rather than letting the owner find later:
99
+
100
+ - **The consent screen says Neon, not their brand**, and the quota is shared.
101
+ Correct for a prototype, wrong once real users sign in. Moving to their own
102
+ Google client is done in the desktop app under **Cloud -> Auth -> Sign-in
103
+ providers**, which also shows the exact redirect URI to register.
104
+ - **GitHub and Vercel have no shared credentials.** Those stay off until the
105
+ owner installs their own client in that same place.