@velven/cli 0.5.0 → 0.6.0
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/CHANGELOG.md +12 -0
- package/README.md +10 -11
- package/dist/velven.js +31 -28
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.0
|
|
4
|
+
|
|
5
|
+
- `--type` and velven.json's `type` and `how_made` are gone: Velven no longer asks what kind of project it is, and the
|
|
6
|
+
page no longer shows a prompt. This version refuses them with a sentence; remove them from velven.json. A new space
|
|
7
|
+
needs only `title` and `devices`. Velven ignores both keys from older versions.
|
|
8
|
+
|
|
9
|
+
## 0.5.1
|
|
10
|
+
|
|
11
|
+
- `velven create credits` names the free allowance as this week's: Velven's builder now refills it weekly, from Monday
|
|
12
|
+
00:00 UTC, instead of daily. The refusal and the low-credit line say "this week" for the same reason. Works with the
|
|
13
|
+
Velven of 0.5.0 and later.
|
|
14
|
+
|
|
3
15
|
## 0.5.0
|
|
4
16
|
|
|
5
17
|
Needs the Velven of the same release, which speaks only this version: update to 0.5.0. 0.3.0 and 0.4.0 can no longer
|
package/README.md
CHANGED
|
@@ -20,19 +20,18 @@ and neither is a `velven.json` in any folder below, whatever its letter case.
|
|
|
20
20
|
```json
|
|
21
21
|
{
|
|
22
22
|
"title": "Star Hop",
|
|
23
|
-
"type": "game",
|
|
24
23
|
"devices": ["desktop", "mobile"],
|
|
25
24
|
"description": "Hop between stars before the light runs out.",
|
|
26
25
|
"engine": "three.js"
|
|
27
26
|
}
|
|
28
27
|
```
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
Two fields are required: `title` and `devices` (`desktop`, `mobile`, `vr`). If one is missing and you're in a
|
|
30
|
+
terminal, `velven publish` asks for it and saves your answer in `velven.json`. Anywhere else, it stops and lists each
|
|
31
|
+
missing field. Since 0.6.0, a `velven.json` that still has `type` or `how_made` is refused with exit 2
|
|
32
|
+
(`invalid_config`): Velven no longer uses them, so remove them and publish again.
|
|
33
33
|
|
|
34
|
-
The optional fields are `description`, `engine`, `ai_tools`, `models
|
|
35
|
-
hosting:
|
|
34
|
+
The optional fields are `description`, `engine`, `ai_tools`, `models` and `source_url`, plus these for hosting:
|
|
36
35
|
|
|
37
36
|
| Key | What it does |
|
|
38
37
|
| --- | --- |
|
|
@@ -139,8 +138,8 @@ with exit 6 if it has one of these in it.
|
|
|
139
138
|
live. A refused version prints the reason. A version Velven couldn't judge prints what Velven saw, and a start hint
|
|
140
139
|
usually fixes it. Either way, you can ask for a review from the printed link. The exception is a program or a miner,
|
|
141
140
|
which is never served: remove the file and publish again.
|
|
142
|
-
- `--title`, `--
|
|
143
|
-
|
|
141
|
+
- `--title`, `--devices desktop,mobile`, `--description` and `--engine` override `velven.json` for this run only.
|
|
142
|
+
`--type` is refused with exit 2: Velven no longer asks what kind of project it is.
|
|
144
143
|
- `--yes` never asks for missing fields. It fails and lists them instead.
|
|
145
144
|
- `--json` prints one JSON object, for scripts and agents. It also works on `whoami` and `versions`.
|
|
146
145
|
|
|
@@ -177,7 +176,7 @@ velven create send "a cozy game where you grow a garden on a floating island"
|
|
|
177
176
|
| `velven create show --timeline` | The newest turn's timeline as a waterfall (see below). `--turn <id>` picks another turn, `--all` lists every tool call |
|
|
178
177
|
| `velven create refs` | The project's reference pictures: each id, when it was added, its size and how many messages carried it. `--project <id>` picks another project |
|
|
179
178
|
| `velven create refs rm <ref-n>` | Delete a reference picture, sent or not. The messages that carried it keep its number, and the number is never given to another picture |
|
|
180
|
-
| `velven create credits` | Your credits:
|
|
179
|
+
| `velven create credits` | Your credits: this week's allowance, your balance, what open turns hold, and the latest entries |
|
|
181
180
|
| `velven create usage` | What each of your turns held and used, newest first. `--project <id>` keeps one project's, `--before <turn>` reads the page before (the last line names it), `--json` prints the page |
|
|
182
181
|
| `velven create stats` | For Velven's admins: p50 and p90 over recent turns. `--days <n>` (1 to 90, 7 by default), `--origin <origin>` |
|
|
183
182
|
|
|
@@ -248,10 +247,10 @@ turn could not take included: "Could not be taken this turn; queued, but the que
|
|
|
248
247
|
with the first: nothing is started or held twice, and the CLI prints "(already sent)" before its line.
|
|
249
248
|
|
|
250
249
|
The CLI says what the turn does as it goes. What the agents write goes to standard output, and their progress goes to
|
|
251
|
-
standard error. When Velven picked the work, it says so in words first ("I'll make the change."; when
|
|
250
|
+
standard error. When Velven picked the work, it says so in words first ("I'll make the change."; when this week's
|
|
252
251
|
credits kept it from a fuller turn, "I'll make the change. I kept this one quick to save credits."), with the work it
|
|
253
252
|
picked and why on standard error. On a build it prints the plain step it is at ("Designing your game", "Painting the art", "Building the world", "Trying it out", "Polishing", "Finishing up"; a later small change says "Making the change" for its work), and on a first build the design as it is settled ("Designing Petal Drift: ..." with its style and palette) and each picture once it lands ("Painted sky (1024 by 512)" with a link to look at it). You see what each step changed, the live link while the game is being built, each playtest's preview
|
|
254
|
-
link (the first playable one, before the turn ends), its screenshots and its clip. When the turn ends, it prints the reply (when the turn asked a question, which it already printed with its choices, only what follows it), then, after a turn that ended with a playable game, "Where it could go next:" with two to four short suggestions for this game and the command that sends the first (`velven create send "Add a boss at the end"`; each is an ordinary message), then the preview link and the credits used. A reply may end with one sentence on what the turn took from your pictures, and with "You're running low on credits for
|
|
253
|
+
link (the first playable one, before the turn ends), its screenshots and its clip. When the turn ends, it prints the reply (when the turn asked a question, which it already printed with its choices, only what follows it), then, after a turn that ended with a playable game, "Where it could go next:" with two to four short suggestions for this game and the command that sends the first (`velven create send "Add a boss at the end"`; each is an ordinary message), then the preview link and the credits used. A reply may end with one sentence on what the turn took from your pictures, and with "You're running low on credits for this week." when what is left would not cover another small change. A turn that stops at
|
|
255
254
|
its credit cap or time limit saves what it finished and ends with exit 0. A failed turn costs nothing. When a new game's
|
|
256
255
|
first build stops before it made the game (it saved nothing, or only its design), the reply ends "Press Build it to try
|
|
257
256
|
again." and the CLI prints `Try again: velven create send --build "Build it"`; "try again" typed after it builds too.
|
package/dist/velven.js
CHANGED
|
@@ -23,7 +23,7 @@ class CliError extends Error {
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
// package.json
|
|
26
|
-
var version = "0.
|
|
26
|
+
var version = "0.6.0";
|
|
27
27
|
|
|
28
28
|
// src/version.ts
|
|
29
29
|
var VERSION = version;
|
|
@@ -2490,7 +2490,7 @@ async function credits(ctx, api, json) {
|
|
|
2490
2490
|
ctx.out(JSON.stringify({ ok: true, credits: view }));
|
|
2491
2491
|
return EXIT.ok;
|
|
2492
2492
|
}
|
|
2493
|
-
ctx.out(`${plural(view.available, "credit")} available: ${view.daily_left} left of
|
|
2493
|
+
ctx.out(`${plural(view.available, "credit")} available: ${view.daily_left} left of this week's allowance, ${view.balance} in your balance, ${view.held} held by open turns.`);
|
|
2494
2494
|
for (const row of view.ledger.slice(0, 10)) {
|
|
2495
2495
|
const amount = row.amount > 0 ? `+${row.amount}` : String(row.amount);
|
|
2496
2496
|
ctx.out(` ${when(row.created_at).padEnd(20)} ${amount.padStart(6)} ${row.kind}${row.turn_id !== null ? ` (turn ${row.turn_id})` : ""}`);
|
|
@@ -3155,7 +3155,6 @@ function mediaProblem(kind, path, size2, bytes, missing = "is not among the file
|
|
|
3155
3155
|
// src/project.ts
|
|
3156
3156
|
import { readdir, readFile as readFile4, writeFile as writeFile3 } from "node:fs/promises";
|
|
3157
3157
|
import { join as join3, win32 } from "node:path";
|
|
3158
|
-
var SPACE_TYPES = ["game", "world", "tool", "wonder"];
|
|
3159
3158
|
var DEVICES = ["desktop", "mobile", "vr"];
|
|
3160
3159
|
var PROJECT_FILE = "velven.json";
|
|
3161
3160
|
var isProjectName = (name) => name.toLowerCase() === PROJECT_FILE;
|
|
@@ -3188,6 +3187,15 @@ async function readProject(dir) {
|
|
|
3188
3187
|
}
|
|
3189
3188
|
return { path, exists: true, data };
|
|
3190
3189
|
}
|
|
3190
|
+
var RETIRED_KEYS = ["type", "how_made"];
|
|
3191
|
+
function refuseRetiredKeys(data) {
|
|
3192
|
+
const found = RETIRED_KEYS.filter((key) => data[key] !== undefined);
|
|
3193
|
+
if (!found.length)
|
|
3194
|
+
return;
|
|
3195
|
+
const named = found.map((key) => `"${key}"`).join(" and ");
|
|
3196
|
+
const sentence = found.length === 1 ? "Remove it and publish again." : "Remove them and publish again.";
|
|
3197
|
+
throw new CliError(`${PROJECT_FILE} has ${named}, which Velven no longer uses. ${sentence}`, EXIT.usage, "invalid_config");
|
|
3198
|
+
}
|
|
3191
3199
|
async function updateProject(project, values) {
|
|
3192
3200
|
for (const [key, value] of Object.entries(values)) {
|
|
3193
3201
|
if (value === undefined)
|
|
@@ -3206,8 +3214,6 @@ function withFlags(data, flags) {
|
|
|
3206
3214
|
const out = { ...data };
|
|
3207
3215
|
if (flags.title !== undefined)
|
|
3208
3216
|
out.title = flags.title;
|
|
3209
|
-
if (flags.type !== undefined)
|
|
3210
|
-
out.type = flags.type.trim().toLowerCase();
|
|
3211
3217
|
if (flags.devices !== undefined)
|
|
3212
3218
|
out.devices = list2(flags.devices);
|
|
3213
3219
|
if (flags.description !== undefined)
|
|
@@ -3216,7 +3222,7 @@ function withFlags(data, flags) {
|
|
|
3216
3222
|
out.engine = flags.engine;
|
|
3217
3223
|
return out;
|
|
3218
3224
|
}
|
|
3219
|
-
var REQUIRED = ["title", "
|
|
3225
|
+
var REQUIRED = ["title", "devices"];
|
|
3220
3226
|
function requiredProblem(field, value) {
|
|
3221
3227
|
if (value === undefined || value === null || value === "" || Array.isArray(value) && value.length === 0)
|
|
3222
3228
|
return "missing";
|
|
@@ -3227,9 +3233,6 @@ function requiredProblem(field, value) {
|
|
|
3227
3233
|
return "must be under 80 characters";
|
|
3228
3234
|
return null;
|
|
3229
3235
|
}
|
|
3230
|
-
if (field === "type") {
|
|
3231
|
-
return typeof value === "string" && SPACE_TYPES.includes(value) ? null : `must be one of ${SPACE_TYPES.join(", ")}`;
|
|
3232
|
-
}
|
|
3233
3236
|
if (!Array.isArray(value) || !value.every((d) => typeof d === "string" && DEVICES.includes(d))) {
|
|
3234
3237
|
return `must be a list from ${DEVICES.join(", ")}, like ["desktop", "mobile"]`;
|
|
3235
3238
|
}
|
|
@@ -3262,7 +3265,6 @@ var OPTIONAL = {
|
|
|
3262
3265
|
engine: [isString, 'must be text, like "three.js"'],
|
|
3263
3266
|
ai_tools: [isStringList, "must be a list of text"],
|
|
3264
3267
|
models: [isStringList, "must be a list of text"],
|
|
3265
|
-
how_made: [(v) => isString(v) && v.length <= 2000, "must be text, under 2000 characters"],
|
|
3266
3268
|
source_url: [isString, "must be a web address"],
|
|
3267
3269
|
entry: [isInsidePath, 'must be a path inside the folder, like "index.html"'],
|
|
3268
3270
|
thumbnail: [isInsidePath, 'must be a path inside the folder, like "media/thumbnail.jpg"'],
|
|
@@ -3293,14 +3295,11 @@ function requiredProblems(data) {
|
|
|
3293
3295
|
}
|
|
3294
3296
|
var QUESTIONS = {
|
|
3295
3297
|
title: "Title, the name people see on Velven: ",
|
|
3296
|
-
type: `Type (${SPACE_TYPES.join(", ")}): `,
|
|
3297
3298
|
devices: `Devices it works on, comma separated (${DEVICES.join(", ")}): `
|
|
3298
3299
|
};
|
|
3299
3300
|
function parseAnswer(field, answer) {
|
|
3300
3301
|
if (field === "title")
|
|
3301
3302
|
return answer.trim();
|
|
3302
|
-
if (field === "type")
|
|
3303
|
-
return answer.trim().toLowerCase();
|
|
3304
3303
|
return list2(answer);
|
|
3305
3304
|
}
|
|
3306
3305
|
async function completeRequired(ctx, project, merged, canAsk) {
|
|
@@ -3865,8 +3864,8 @@ var DONE = ["live", "refused", "unjudged", "replaced", "preview"];
|
|
|
3865
3864
|
var POLL_MS = 3000;
|
|
3866
3865
|
var WAIT_LIMIT_MS = 20 * 60 * 1000;
|
|
3867
3866
|
function listingFields(config) {
|
|
3868
|
-
const fields = { title: config.title,
|
|
3869
|
-
for (const key of ["description", "engine", "ai_tools", "models", "
|
|
3867
|
+
const fields = { title: config.title, devices: config.devices };
|
|
3868
|
+
for (const key of ["description", "engine", "ai_tools", "models", "source_url"]) {
|
|
3870
3869
|
if (config[key] !== undefined)
|
|
3871
3870
|
fields[key] = config[key];
|
|
3872
3871
|
}
|
|
@@ -3925,6 +3924,7 @@ async function publish(ctx, options) {
|
|
|
3925
3924
|
const creds = await findToken(ctx, base);
|
|
3926
3925
|
const guest = !creds;
|
|
3927
3926
|
const project = await readProject(dir);
|
|
3927
|
+
refuseRetiredKeys(project.data);
|
|
3928
3928
|
const merged = withFlags(project.data, options);
|
|
3929
3929
|
const shape = optionalProblems(merged);
|
|
3930
3930
|
if (shape.length)
|
|
@@ -4209,6 +4209,7 @@ async function reset(ctx, options) {
|
|
|
4209
4209
|
}
|
|
4210
4210
|
|
|
4211
4211
|
// src/cli.ts
|
|
4212
|
+
var TYPE_FLAG_GONE = "--type is no longer an option: Velven no longer asks what kind of project it is. Run the command without it.";
|
|
4212
4213
|
var HELP = `velven ${VERSION}: publish a folder to Velven.
|
|
4213
4214
|
|
|
4214
4215
|
Usage:
|
|
@@ -4220,7 +4221,6 @@ Usage:
|
|
|
4220
4221
|
(not signed in: an unlisted page with a claim token, no --prod)
|
|
4221
4222
|
--wait Wait for the preview link, or with --prod until it is live
|
|
4222
4223
|
--title <text> Override velven.json for this run
|
|
4223
|
-
--type <type> game, world, tool or wonder
|
|
4224
4224
|
--devices <list> desktop, mobile, vr (comma separated)
|
|
4225
4225
|
--description <text>
|
|
4226
4226
|
--engine <engine>
|
|
@@ -4261,18 +4261,21 @@ var COMMANDS = {
|
|
|
4261
4261
|
description: "string",
|
|
4262
4262
|
engine: "string"
|
|
4263
4263
|
},
|
|
4264
|
-
run: (ctx, p) =>
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4264
|
+
run: (ctx, p) => {
|
|
4265
|
+
if (p.flags.type !== undefined)
|
|
4266
|
+
throw new CliError(TYPE_FLAG_GONE, EXIT.usage, "usage");
|
|
4267
|
+
return publish(ctx, {
|
|
4268
|
+
dir: p.positionals[0],
|
|
4269
|
+
prod: p.flags.prod === true,
|
|
4270
|
+
wait: p.flags.wait === true,
|
|
4271
|
+
yes: p.flags.yes === true,
|
|
4272
|
+
json: p.flags.json === true,
|
|
4273
|
+
title: stringFlag(p, "title"),
|
|
4274
|
+
devices: stringFlag(p, "devices"),
|
|
4275
|
+
description: stringFlag(p, "description"),
|
|
4276
|
+
engine: stringFlag(p, "engine")
|
|
4277
|
+
});
|
|
4278
|
+
}
|
|
4276
4279
|
},
|
|
4277
4280
|
versions: {
|
|
4278
4281
|
flags: { json: "boolean", space: "string" },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velven/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Publish a folder to Velven, the community marketplace for spaces built with AI: a private preview first, then hosted and live on its own page in seconds.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://velven.ai/docs",
|