@mirage-cli/contentdynamite-cli 0.0.0-stage → 1.0.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 +7 -0
- package/LICENSE +21 -0
- package/README.md +72 -2
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +7 -0
- package/dist/client.d.ts +24 -0
- package/dist/commands/articles.d.ts +7 -0
- package/dist/commands/auth.d.ts +6 -0
- package/dist/commands/batches.d.ts +3 -0
- package/dist/commands/evals.d.ts +3 -0
- package/dist/commands/gbp.d.ts +3 -0
- package/dist/commands/images.d.ts +9 -0
- package/dist/commands/landing-pages.d.ts +3 -0
- package/dist/commands/profiles.d.ts +4 -0
- package/dist/commands/revisions.d.ts +4 -0
- package/dist/commands/tokens.d.ts +3 -0
- package/dist/commands/users.d.ts +3 -0
- package/dist/config.d.ts +6 -0
- package/dist/context.d.ts +13 -0
- package/dist/credentials.d.ts +23 -0
- package/dist/files.d.ts +3 -0
- package/dist/helpers.d.ts +27 -0
- package/dist/index-b00ea6js.js +2158 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +10 -0
- package/dist/jobs.d.ts +4 -0
- package/dist/options.d.ts +8 -0
- package/dist/output.d.ts +10 -0
- package/dist/program.d.ts +4 -0
- package/dist/version.d.ts +1 -0
- package/dist/wire.d.ts +64 -0
- package/package.json +59 -4
package/CHANGELOG.md
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alex Brufsky
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,73 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @mirage-cli/contentdynamite-cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`ve-dynamite`, the Content Dynamite CLI: SEO articles, Google Business Profile posts, BOFU landing pages, company profiles, ICP, design systems, part-by-part revisions and featured images, over the Content Dynamite `/api/v1` JSON API. It talks to `https://content-dynamite.alexthecomputer1.workers.dev` unless `--url` or `VE_DYNAMITE_API_URL` names another host.
|
|
4
|
+
|
|
5
|
+
Stdout is always indented JSON, so pipe it to `jq`. Warnings, progress and hints go to stderr. The exit code is 0 on success and 1 on any error.
|
|
6
|
+
|
|
7
|
+
> **Money warning:** several commands spend real money (LLM, SERP, crawling, image credits) on every call, and nothing rate limits them. Their help says so and they print a warning when they run. Never script them in a loop.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm i -g @mirage-cli/contentdynamite-cli # or: bun add -g @mirage-cli/contentdynamite-cli
|
|
13
|
+
ve-dynamite --help
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Runs on Node 18+ or bun. The only dependencies are `commander` and `zod`.
|
|
17
|
+
|
|
18
|
+
## Sign in
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
ve-dynamite login --email you@example.com --password <pw> # stores a 24h JWT in ~/.config/ve-dynamite/
|
|
22
|
+
ve-dynamite whoami
|
|
23
|
+
ve-dynamite tokens create --name ci-runner [--expires-days 30] # a long lived ved_ token for unattended use
|
|
24
|
+
ve-dynamite tokens use ved_xxxxxxxx
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Or pass a token per call: `--token ved_xxx` or `VE_DYNAMITE_TOKEN=ved_xxx`. Precedence is `--token`, then `VE_DYNAMITE_TOKEN`, then the stored login. `VE_DYNAMITE_CONFIG_DIR` moves the stored login.
|
|
28
|
+
|
|
29
|
+
## Commands
|
|
30
|
+
|
|
31
|
+
| Group | Commands |
|
|
32
|
+
|---|---|
|
|
33
|
+
| root | `login`, `logout`, `whoami`, `upload <file> --type image\|csv` |
|
|
34
|
+
| `tokens` | `create`, `list`, `revoke <id>`, `use <token>` |
|
|
35
|
+
| `users` (admins) | `list`, `set-role <id> --role admin\|normal` |
|
|
36
|
+
| `profiles` | `create`, `list`, `get`, `update`, `delete` |
|
|
37
|
+
| `icp` | `show`, `export`, `regenerate`, `update` |
|
|
38
|
+
| `categories` | `list`, `add` |
|
|
39
|
+
| `design-system` | `show`, `set <profile-id> <zip>`, `rescan`, `clear` |
|
|
40
|
+
| `articles` | `write`, `research-prompt`, `get`, `list`, `watch`, `update`, `delete`, `download`, `export`, `parts`, `reject`, `revisions`, `provenance-audit`, `clean-provenance`, `embed-audit`, `strip-embeds` |
|
|
41
|
+
| `revisions` | `list`, `get`, `watch`, `verdict` |
|
|
42
|
+
| `batches` | `create`, `list`, `get`, `watch`, `delete` |
|
|
43
|
+
| `gbp` | `write`, `batch`, `list`, `get`, `watch`, `update`, `regenerate`, `delete`, `image-options` |
|
|
44
|
+
| `landing-pages` | `write`, `get`, `list`, `watch`, `update`, `delete`, `export`, `deck`, `versions`, `research`, `audit`, `reiterate`, `new-version`, `criteria`, `fix-placeholders`, `fix-images`, `rebrand` |
|
|
45
|
+
| `evals` | `run` |
|
|
46
|
+
| `images` | `edit`, `status`, `commit`, `watch` |
|
|
47
|
+
|
|
48
|
+
`ve-dynamite <group> <command> --help` documents every flag.
|
|
49
|
+
|
|
50
|
+
Paid, real money per item: `articles write|reject`, `articles clean-provenance --rewrite`, `batches create`, `gbp write|batch|regenerate`, `landing-pages write|reiterate|new-version|fix-images`, `profiles create`, `icp regenerate`, `design-system set|rescan`, `images edit`, `evals run --judge`.
|
|
51
|
+
|
|
52
|
+
Generation is asynchronous. A write returns an id at once; `watch` polls until it finishes, or poll `get` yourself about every 10 seconds. A completed article or landing page is `success`, a GBP post is `done`, a revision is `ready`.
|
|
53
|
+
|
|
54
|
+
## Running inside a host
|
|
55
|
+
|
|
56
|
+
`buildProgram(options)` returns an unparsed Commander program and `run(argv, options)` runs one command and returns its exit code:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import { buildProgram, run, type CostReport } from "@mirage-cli/contentdynamite-cli";
|
|
60
|
+
|
|
61
|
+
const program = buildProgram({ mount: true, reportCost: (report: CostReport) => record(report) });
|
|
62
|
+
const code = await run(["articles", "get", "41"], { mount: true });
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`mount: true` is for agent hosts that inject the credential and the API host. It drops `login`, `logout`, `tokens use`, every `watch` and the `--url` flag, never reads or writes the stored login, takes the token only from `--token` or `VE_DYNAMITE_TOKEN`, requires `-o` on downloads, and words its hints for an agent that polls.
|
|
66
|
+
|
|
67
|
+
`reportCost` is called once the API accepts paid work, with `{ provider: "contentdynamite", units }`, where `units` is the number of paid jobs started. It is never called for a request the API refused.
|
|
68
|
+
|
|
69
|
+
File arguments and `-o` targets go through the Mirage VFS bridge (`globalThis.__MIRAGE_CLI_FILE_IO__`) when one is installed and claims the path, and otherwise through `node:fs`. For a Mirage mount, use [`@mirage-cli/contentdynamite`](../contentdynamite), which does all of this.
|
|
70
|
+
|
|
71
|
+
## Where the source lives
|
|
72
|
+
|
|
73
|
+
The CLI is developed next to the API in the Content Dynamite backend repository, which is private, and imports the API's own request schemas and response types so it cannot drift from it. `src/` here is generated from that repository by `bun run cli:mirage --out <this folder>`: the CLI files are copied as they are, the schemas and status helpers it uses are bundled into `src/wire.js`, and their types are written out flat to `src/wire.d.ts`. `src/source.json` names the commit it came from. Change the CLI there and regenerate, rather than editing `src/` by hand.
|
package/dist/bin.d.ts
ADDED
package/dist/bin.js
ADDED
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type Params = Record<string, string | number | boolean | null | undefined>;
|
|
2
|
+
export declare class ApiError extends Error {
|
|
3
|
+
readonly status: number;
|
|
4
|
+
constructor(status: number, message: string);
|
|
5
|
+
}
|
|
6
|
+
export declare class ApiClient {
|
|
7
|
+
readonly baseUrl: string;
|
|
8
|
+
readonly token: string;
|
|
9
|
+
constructor(baseUrl: string, token: string);
|
|
10
|
+
get<T = unknown>(path: string, params?: Params): Promise<T>;
|
|
11
|
+
getText(path: string, params?: Params): Promise<string>;
|
|
12
|
+
getDownload(path: string, params?: Params): Promise<{
|
|
13
|
+
content: Uint8Array;
|
|
14
|
+
filename: string | null;
|
|
15
|
+
}>;
|
|
16
|
+
post<T = unknown>(path: string, body?: unknown, params?: Params): Promise<T>;
|
|
17
|
+
postForm<T = unknown>(path: string, file: Blob, filename: string, params?: Params): Promise<T>;
|
|
18
|
+
put<T = unknown>(path: string, body?: unknown): Promise<T>;
|
|
19
|
+
patch<T = unknown>(path: string, body?: unknown): Promise<T>;
|
|
20
|
+
delete<T = unknown>(path: string): Promise<T>;
|
|
21
|
+
private json;
|
|
22
|
+
private request;
|
|
23
|
+
private url;
|
|
24
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import type { ApiClient, Params } from '../client.js';
|
|
3
|
+
import type { Cli } from '../context.js';
|
|
4
|
+
import { type ArticleDoneRes, type ArticlePendingRes } from '../wire.js';
|
|
5
|
+
export type ArticleRes = ArticleDoneRes | ArticlePendingRes;
|
|
6
|
+
export declare function everyArticle(client: ApiClient, filter: Params): Promise<ArticleRes[]>;
|
|
7
|
+
export declare function registerArticles(program: Command, cli: Cli): Command;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import { ApiClient } from '../client.js';
|
|
3
|
+
import type { Cli } from '../context.js';
|
|
4
|
+
import type { UserRes } from '../wire.js';
|
|
5
|
+
export declare function signedIn(client: ApiClient, rejected: string): Promise<UserRes>;
|
|
6
|
+
export declare function registerAuth(program: Command, cli: Cli): void;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import type { ApiClient } from '../client.js';
|
|
3
|
+
import type { Cli } from '../context.js';
|
|
4
|
+
import type { EditFeatImageJobRes } from '../wire.js';
|
|
5
|
+
export declare function loadJobCache(): Record<string, EditFeatImageJobRes>;
|
|
6
|
+
export declare function cacheJob(key: string, job: EditFeatImageJobRes): void;
|
|
7
|
+
export declare function fetchStatus(client: ApiClient, articleId: number, jobId: string, cached?: boolean): Promise<EditFeatImageJobRes>;
|
|
8
|
+
export declare function registerImages(program: Command, cli: Cli): void;
|
|
9
|
+
export declare function registerUpload(program: Command, cli: Cli): void;
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const DEFAULT_API_URL = "https://content-dynamite.alexthecomputer1.workers.dev";
|
|
2
|
+
export declare function configDir(): string;
|
|
3
|
+
export declare const credentialsPath: () => string;
|
|
4
|
+
export declare const imageJobsPath: () => string;
|
|
5
|
+
export declare function defaultApiUrl(): string;
|
|
6
|
+
export declare function envToken(): string | undefined;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Command } from 'commander';
|
|
2
|
+
import { ApiClient } from './client.js';
|
|
3
|
+
import { type ResolvedToken } from './credentials.js';
|
|
4
|
+
import type { ProgramOptions } from './options.js';
|
|
5
|
+
export interface Cli {
|
|
6
|
+
mount: boolean;
|
|
7
|
+
baseUrl(): string;
|
|
8
|
+
token(): ResolvedToken;
|
|
9
|
+
client(): ApiClient;
|
|
10
|
+
follow(watch: string, get: string): string;
|
|
11
|
+
billed(units: number): void;
|
|
12
|
+
}
|
|
13
|
+
export declare function cliFor(program: Command, { mount, reportCost }: ProgramOptions): Cli;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const PAT_PREFIX = "ved_";
|
|
3
|
+
declare const storedCredentials: z.ZodObject<{
|
|
4
|
+
authToken: z.ZodString;
|
|
5
|
+
email: z.ZodString;
|
|
6
|
+
username: z.ZodString;
|
|
7
|
+
profile: z.ZodNullable<z.ZodString>;
|
|
8
|
+
expiresAt: z.ZodNullable<z.ZodNumber>;
|
|
9
|
+
}, z.core.$strip>;
|
|
10
|
+
export type StoredCredentials = z.output<typeof storedCredentials>;
|
|
11
|
+
export interface ResolvedToken {
|
|
12
|
+
token: string;
|
|
13
|
+
source: 'flag' | 'env' | 'stored';
|
|
14
|
+
credentials: StoredCredentials | null;
|
|
15
|
+
}
|
|
16
|
+
export declare const isPat: (token: string) => boolean;
|
|
17
|
+
export declare function loadCredentials(): StoredCredentials | null;
|
|
18
|
+
export declare function saveCredentials({ authToken, email, username, profile, expiresAt }: StoredCredentials): void;
|
|
19
|
+
export declare function clearCredentials(): void;
|
|
20
|
+
export declare function decodeJwtPayload(token: string): Record<string, unknown> | null;
|
|
21
|
+
export declare function decodeJwtExp(token: string): number | null;
|
|
22
|
+
export declare function resolveToken(flagToken?: string, stored?: boolean): ResolvedToken;
|
|
23
|
+
export {};
|
package/dist/files.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type Command, CommanderError } from 'commander';
|
|
2
|
+
import type { Cli } from './context.js';
|
|
3
|
+
export declare class Exit extends CommanderError {
|
|
4
|
+
constructor(code: number);
|
|
5
|
+
}
|
|
6
|
+
export declare const clock: {
|
|
7
|
+
sleep: (ms: number) => Promise<void>;
|
|
8
|
+
now: () => number;
|
|
9
|
+
};
|
|
10
|
+
export declare const fmtNum: (value: number) => string;
|
|
11
|
+
export declare const splitCsv: (value: string) => string[];
|
|
12
|
+
export declare function integer(value: string): number;
|
|
13
|
+
export declare function decimal(value: string): number;
|
|
14
|
+
export declare function between(low: number, high: number): (value: string) => number;
|
|
15
|
+
export declare function either(value: string | undefined, file: string | undefined, flags: string): string | undefined;
|
|
16
|
+
export declare function documentOutput(command: Command, cli: Cli): Command;
|
|
17
|
+
export declare function downloadDocument(cli: Cli, path: string, format: string, output: string | undefined, fallback: string): Promise<string>;
|
|
18
|
+
export declare function interactiveFields(value: string | boolean | undefined): Record<string, string | boolean>;
|
|
19
|
+
export interface Watch<T> {
|
|
20
|
+
interval: number;
|
|
21
|
+
timeout?: number;
|
|
22
|
+
poll: () => Promise<T>;
|
|
23
|
+
progress: (item: T) => string;
|
|
24
|
+
done: (item: T) => boolean;
|
|
25
|
+
timedOut: (item: T) => string;
|
|
26
|
+
}
|
|
27
|
+
export declare function watchLoop<T>({ interval, timeout, poll, progress, done, timedOut }: Watch<T>): Promise<T>;
|