@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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
|
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.
|