create-stellar-service 0.0.0-stage → 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Stellar Agent Services contributors
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,143 @@
1
- # Temporary Holding Version
1
+ # create-stellar-service
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ `npm create stellar-service` makes a project that sells an AI service on Stellar: agents pay per call in USDC through
4
+ x402, under [Provider Standard v1](../../docs/provider-standard.md). The project runs, takes a paid testnet call and
5
+ goes live as it comes, with no wallet needed until it goes live.
6
+
7
+ ```text
8
+ $ npm create stellar-service
9
+
10
+ Project folder [my-stellar-service]: captioner
11
+ What are you making?
12
+ 1. Wrap an API you already run, in any language (no code changes)
13
+ 2. A new service in TypeScript
14
+ 3. A new service in Python (FastAPI)
15
+ Pick 1, 2 or 3 [1]: 1
16
+ Your API's OpenAPI document, or the URL a paid call goes to [http://127.0.0.1:8000/openapi.json]:
17
+ In one sentence, what does it offer? Agents search for it by these words and its name.
18
+ [Enter: the document's]: Captions for product photos, in English and Turkish.
19
+ What does a call cost, in USDC? [0.01]: 0.05
20
+ ```
21
+
22
+ Then it writes the project and installs it. Next:
23
+
24
+ ```bash
25
+ cd captioner
26
+ npm run dev # runs it here, paid to a throwaway testnet account: no wallet needed yet
27
+ npm run try # pays for one call with test money, from another terminal
28
+ npm run launch # goes live: one approval in your wallet, then Fly.io
29
+ ```
30
+
31
+ `npm run launch` opens a page where your own wallet (Freighter, xBull, Albedo and others) approves the USDC trustline,
32
+ the Stellar 8004 registration and a request for verification, in one go. Then it saves `PAY_TO` and `AGENT_ID`,
33
+ deploys to Fly.io if you say yes, and waits until the service answers at its public address. `npm run status` shows
34
+ where it stands at any time.
35
+
36
+ The project depends on the Provider Kit released with this command. To try changes not released yet, run it from a
37
+ clone of the repository, built once (`npm install && npm run build`):
38
+
39
+ ```bash
40
+ node packages/create-stellar-service/dist/index.js captioner
41
+ ```
42
+
43
+ From a clone, it packs the Provider Kit into the project's `vendor/` folder, so the project installs and builds by
44
+ itself, in Docker too.
45
+
46
+ ## What it makes
47
+
48
+ | Kind | You get |
49
+ | --- | --- |
50
+ | `wrap` | The [gateway](../provider-kit/README.md#gateway-mode) in front of an API you already run, in any language, with no code changes. With an OpenAPI document (FastAPI serves `/openapi.json`), `provider-kit init --openapi` makes a service of each POST endpoint it can call; without one, a starter service calls the URL you give. |
51
+ | `typescript` | A new service with the [Provider Kit library](../provider-kit/README.md#library-in-short): `src/server.ts` and an example service to replace, `src/services/summarize.ts`. |
52
+ | `python` | A new FastAPI app, `app.py`, with the gateway in front of it, and one container that runs both. |
53
+
54
+ Every project has `.env` (the settings, kept out of git) and `.env.example`, a `Dockerfile` that keeps the job
55
+ database on a volume at `/data`, a `fly.toml` for Fly.io, a `README.md`, and these scripts:
56
+
57
+ | Script | What it does |
58
+ | --- | --- |
59
+ | `npm run dev` | Starts the service, or the gateway (and the Python kind's API), at http://localhost:8787. With `PAY_TO` empty it is paid to a throwaway testnet account ([`provider-kit dev`](../provider-kit/README.md#the-throwaway-payout-account)) |
60
+ | `npm run try` | Pays for one call with test money, from a throwaway testnet account, and starts the service for it when it isn't running ([`provider-kit try`](../provider-kit/README.md#trying-a-paid-call)) |
61
+ | `npm run check` | Checks the provider against the standard and shows how agents see it |
62
+ | `npm run launch` | Takes it live: one page in your wallet for the trustline, the 8004 registration and verification, then the deploy ([`provider-kit launch`](../provider-kit/README.md#launching)) |
63
+ | `npm run status` | Where it stands, from running here to verified, and the command to run next |
64
+ | `npm run deploy` | Deploys again to Fly.io (`fly deploy`) |
65
+ | `npm start` | Starts it for production: `PUBLIC_ORIGIN` must be your HTTPS origin |
66
+
67
+ A gateway config reads `baseUrl`, `payTo`, `store` and the agent id from `.env` (`env:PUBLIC_ORIGIN`, `env:PAY_TO`,
68
+ `env:JOB_DB_PATH`, `env:AGENT_ID`), so the same file runs on a laptop and in a container.
69
+
70
+ **Fly.io.** `fly.toml` names the app after the folder, so the public address, `https://<app>.fly.dev`, is known before
71
+ the first deploy: `launch` registers the service with it, writes `PUBLIC_ORIGIN`, `PAY_TO` and `AGENT_ID` under
72
+ `[env]`, and deploys once. The job database lives on a volume Fly makes on the first deploy. Another host works too:
73
+ `npm run launch -- --url https://your.domain --no-deploy`.
74
+
75
+ **Python.** With [uv](https://docs.astral.sh/uv/) installed, `npm run dev` starts the API with
76
+ `uv run --with-requirements requirements.txt`, so there is no virtual environment to make. Without it, the next steps
77
+ give the two commands that make `.venv`.
78
+
79
+ ## Mainnet
80
+
81
+ `--network mainnet` makes a project that takes real USDC on `stellar:pubnet`. What changes:
82
+
83
+ - **The facilitator.** Payments settle through OpenZeppelin's facilitator, `https://channels.openzeppelin.com/x402`,
84
+ which Stellar's docs list for mainnet (x402.org's settles testnet only). It wants an API key: get one at
85
+ [channels.openzeppelin.com/gen](https://channels.openzeppelin.com/gen) and set `FACILITATOR_AUTH="Bearer <key>"` in
86
+ `.env`. A gateway config sends it as `"facilitatorHeaders": { "Authorization": "env:FACILITATOR_AUTH" }`; the
87
+ TypeScript service reads it in `src/server.ts`. Without it, the service stops at start and says so.
88
+ - **The RPC.** `SOROBAN_RPC_URL` in `.env` starts as `https://mainnet.sorobanrpc.com`, the RPC the Stellar 8004 SDK
89
+ uses. Mainnet has no default one. For production traffic, an [RPC provider](https://developers.stellar.org/docs/data/apis/rpc/providers)'s
90
+ is safer.
91
+ - **No test money.** There is no throwaway account and no `npm run try`. `npm run launch` sets up the account clients
92
+ pay in your own wallet, which needs a few XLM, and `npm run dev` runs once `PAY_TO` is set.
93
+ - **The deploy.** `launch` stages `FACILITATOR_AUTH` and `SOROBAN_RPC_URL` as Fly secrets. A TypeScript service reads its
94
+ network from `STELLAR_NETWORK`, which `launch` also writes to `fly.toml`. It won't call a deploy live while it takes
95
+ payments on another network than the one it registered on.
96
+
97
+ To take a testnet project to mainnet, make these changes by hand: in a gateway config, `network`, `facilitator`,
98
+ `facilitatorHeaders` and `rpcUrl`; in a TypeScript project's `.env`, `STELLAR_NETWORK=stellar:pubnet`. In both, add
99
+ `FACILITATOR_AUTH` and `SOROBAN_RPC_URL` to `.env`. Then empty `PAY_TO` and `AGENT_ID` (testnet's agent ids don't exist
100
+ on mainnet), delete `DEV_PAY_TO`, and run `npm run launch`.
101
+
102
+ ## For scripts and coding agents
103
+
104
+ Flags answer every question. Without a terminal it asks nothing, and `--kind` is needed:
105
+
106
+ ```bash
107
+ create-stellar-service captioner --kind wrap --openapi http://127.0.0.1:8000/openapi.json \
108
+ --description "Captions for product photos, in English and Turkish." --price 0.05
109
+ ```
110
+
111
+ | Flag | Answers |
112
+ | --- | --- |
113
+ | `--kind wrap\|typescript\|python` | What to make |
114
+ | `--openapi <file \| url>` | The API's OpenAPI document (`wrap`). It is read first, so nothing is written for an API that isn't running. With a file whose servers are relative, `--upstream` is the API's base URL. |
115
+ | `--upstream <url>` | Without `--openapi`: the URL a paid call goes to (`wrap`) |
116
+ | `--description <text>` | One sentence on what it offers: search matches it and the name |
117
+ | `--name <text>` | The provider's name. Default: the folder's, or the document's title. |
118
+ | `--price <usdc>` | What a call costs, in USDC. Default `0.01`. |
119
+ | `--network testnet\|mainnet` | Where clients pay. Default `testnet`, with test money. `mainnet` takes real USDC: see [Mainnet](#mainnet). |
120
+ | `--wallet`, `--pay-to <G…>` | Set up the payout account now in your wallet, or use an address you have. Without either, nothing is needed until `npm run launch`. |
121
+ | `--yes` | Take the defaults for the questions no flag answers |
122
+
123
+ `--wallet` opens a page in the user's browser, so a coding agent asks the user first. It signs nothing itself. So
124
+ does `npm run launch` later: a coding agent runs it with `--no-open` and gives the user the link.
125
+
126
+ ## Safety
127
+
128
+ - **No secret key, anywhere.** It never asks for one, makes one or writes one. The payout account is set up in the
129
+ developer's own wallet, on `provider-kit launch`'s approval page, and only its public address is saved. Until then,
130
+ `npm run dev`'s throwaway testnet account has no key anyone keeps.
131
+ - **It never pays.** `npm run try` pays later, when the developer runs it, from a throwaway account with test money.
132
+ - **Testnet unless asked.** Every project starts on `stellar:testnet`; only `--network mainnet` makes a mainnet one.
133
+ - **Nothing is overwritten.** It refuses a folder that already has files in it.
134
+
135
+ ## Files
136
+
137
+ | File | Contents |
138
+ | --- | --- |
139
+ | `src/main.ts` | The questions, the flags, and the run: write, install, the gateway config, the payout account if asked |
140
+ | `src/files.ts` | The files each kind writes: `package.json` and its scripts, `.env`, the README, the Python kind's gateway config, and the templates filled in |
141
+ | `src/kit.ts` | Where the Provider Kit comes from: a clone (packed into `vendor/`) or npm, and the npm overrides a project needs |
142
+ | `src/index.ts` | The command |
143
+ | `templates/` | The code and Dockerfiles each kind starts from, and `fly.toml` |
@@ -0,0 +1,133 @@
1
+ export type Kind = "wrap" | "typescript" | "python";
2
+ /** The network a project takes payments on, as its CAIP-2 id: testnet's test money, or mainnet's (pubnet) real USDC. */
3
+ export type Network = "stellar:testnet" | "stellar:pubnet";
4
+ /** The facilitator that verifies and settles a project's payments: x402.org's on testnet, OpenZeppelin's on mainnet. */
5
+ export declare const FACILITATORS: Record<Network, string>;
6
+ /** Where mainnet's facilitator gives out API keys. */
7
+ export declare const FACILITATOR_KEYS = "https://channels.openzeppelin.com/gen";
8
+ /** The Soroban RPC a mainnet project starts with: the one the Stellar 8004 SDK reads the registry through. */
9
+ export declare const MAINNET_RPC = "https://mainnet.sorobanrpc.com";
10
+ /** What a mainnet project also reads from .env, kept as secrets: the facilitator's API key, and an RPC URL, which may hold one. */
11
+ export declare const MAINNET_SETTINGS: readonly ["FACILITATOR_AUTH", "SOROBAN_RPC_URL"];
12
+ export declare const KINDS: Record<Kind, string>;
13
+ /** What every kind's provider says until the developer writes their own: the template's service. */
14
+ export declare const TEMPLATE_DESCRIPTION = "Summarizes English text for agents: the first sentences, with sentence and word counts.";
15
+ /** The price a service starts with, in USDC, when none is given. */
16
+ export declare const DEFAULT_PRICE = "0.01";
17
+ /** The template service, as the Python kind's gateway config describes it. src/services/summarize.ts and app.py match it. */
18
+ export declare const SUMMARIZE: {
19
+ name: string;
20
+ title: string;
21
+ description: string;
22
+ price: string;
23
+ charge: string;
24
+ expectedDurationSeconds: number;
25
+ inputSchema: {
26
+ type: string;
27
+ required: string[];
28
+ properties: {
29
+ text: {
30
+ type: string;
31
+ minLength: number;
32
+ maxLength: number;
33
+ description: string;
34
+ };
35
+ sentences: {
36
+ type: string;
37
+ minimum: number;
38
+ maximum: number;
39
+ default: number;
40
+ description: string;
41
+ };
42
+ };
43
+ additionalProperties: boolean;
44
+ };
45
+ outputSchema: {
46
+ type: string;
47
+ required: string[];
48
+ properties: {
49
+ summary: {
50
+ type: string;
51
+ };
52
+ sentences: {
53
+ type: string;
54
+ description: string;
55
+ };
56
+ words: {
57
+ type: string;
58
+ description: string;
59
+ };
60
+ };
61
+ };
62
+ examples: {
63
+ input: {
64
+ text: string;
65
+ sentences: number;
66
+ };
67
+ output: {
68
+ summary: string;
69
+ sentences: number;
70
+ words: number;
71
+ };
72
+ }[];
73
+ capacity: {
74
+ concurrency: number;
75
+ maxQueueWaitSeconds: number;
76
+ };
77
+ upstream: {
78
+ url: string;
79
+ };
80
+ };
81
+ /** The settings every project reads from .env, which a gateway config names as "env:NAME". */
82
+ export declare const SETTINGS: readonly ["PAY_TO", "PUBLIC_ORIGIN", "JOB_DB_PATH", "AGENT_ID"];
83
+ export interface Project {
84
+ kind: Kind;
85
+ /** The folder's name, which is also the npm package name and the Docker image name. */
86
+ package: string;
87
+ /** The Fly.io app name in fly.toml: its public address is https://<app>.fly.dev. */
88
+ app: string;
89
+ name: string;
90
+ description: string;
91
+ /** Each service's price in USDC, such as "0.01". */
92
+ price: string;
93
+ network: Network;
94
+ /** Whether the kit's packages are in vendor/, packed from a clone. */
95
+ vendored: boolean;
96
+ /** Python: whether uv runs the API, so no virtual environment has to be made first. */
97
+ uv?: boolean;
98
+ }
99
+ /** The command that starts the Python kind's API on this computer: with uv when it is installed, else from .venv. */
100
+ export declare function apiCommand(project: Pick<Project, "uv">): string;
101
+ /** A file from templates/<directory>/, with every {{placeholder}} filled in. */
102
+ export declare function template(directory: string, file: string, project: Project): string;
103
+ /** The template files each kind copies: [template directory, file]. */
104
+ export declare const TEMPLATE_FILES: Record<Kind, [string, string][]>;
105
+ export declare function packageJson(project: Project, dependencies: Record<string, string>): string;
106
+ /** The Python kind's gateway config: the template service in front of app.py, at the project's price. */
107
+ export declare function pythonGatewayConfig(project: Project): string;
108
+ /**
109
+ * A config `provider-kit init` wrote, made to run anywhere: baseUrl, store and the agent id from .env, no "dev" (npm run
110
+ * dev runs in development), the name, description and price given, and on mainnet its facilitator, key and RPC. Returns
111
+ * the config and the other variables it reads, such as the API key init named for an upstream header.
112
+ */
113
+ export declare function adaptInitConfig(text: string, provider: {
114
+ name?: string;
115
+ description?: string;
116
+ price?: string;
117
+ }, network?: Network): {
118
+ config: string;
119
+ variables: string[];
120
+ };
121
+ /** .env, or .env.example with `example`: the settings, and the variables a gateway config also reads. */
122
+ export declare function envFile(project: Project, values: {
123
+ payTo?: string;
124
+ variables?: string[];
125
+ }, example?: boolean): string;
126
+ export declare function gitignore(project: Project): string;
127
+ export declare function dockerignore(project: Project): string;
128
+ /** The project's README: run it, try it, take it live, see where it stands, and make it yours. */
129
+ export declare function readme(project: Project, options: {
130
+ services: string;
131
+ upstream?: string;
132
+ }): string;
133
+ //# sourceMappingURL=files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../src/files.ts"],"names":[],"mappings":"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"}