stml-cli 0.1.0__tar.gz

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.
@@ -0,0 +1,86 @@
1
+ Metadata-Version: 2.4
2
+ Name: stml-cli
3
+ Version: 0.1.0
4
+ Summary: The stml partner CLI — pull, edit, push, and publish stml apps and libraries.
5
+ Author: stml
6
+ Project-URL: Homepage, https://stml.io
7
+ Requires-Python: >=3.11
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: certifi
10
+
11
+ # stml — the partner CLI
12
+
13
+ Pull an app you built on the platform down to a local folder, edit it with your
14
+ own editor and git, push it back to test, and publish a finished version to the
15
+ App Store — without cloning the stml4 monorepo.
16
+
17
+ Spec: [`docs/user-stories/feature/024-app-authoring-platform.md`](../docs/user-stories/feature/024-app-authoring-platform.md).
18
+
19
+ ## Requirements
20
+
21
+ - **Python 3.11 or newer** on your PATH. That's the only hard requirement.
22
+ - macOS: `brew install python@3.12` (or python.org). **Not** the system
23
+ `/usr/bin/python3` — it's often 3.9, which is too old.
24
+ - Linux: your distro's Python 3.11+, or `pipx`/`pyenv`/`uv`.
25
+ - Windows 10/11: the python.org installer (gives you the `py` launcher).
26
+
27
+ ## Install
28
+
29
+ | Method | Command |
30
+ |---|---|
31
+ | **pipx** (recommended) | `pipx install stml-cli` |
32
+ | pip (in a venv) | `pip install stml-cli` |
33
+ | single file (no installer) | `curl -fsSL <backend>/cli/stml -o stml && chmod +x stml` |
34
+
35
+ Runs on **macOS, Linux, and Windows** — it's pure-Python and touches nothing
36
+ platform-specific beyond the filesystem and HTTPS.
37
+
38
+ ## Quickstart
39
+
40
+ ```sh
41
+ # Point at your backend (default: http://localhost:8010)
42
+ export STML4_BACKEND=https://api.stml.io
43
+
44
+ # 1. Sign in — opens your browser; approve, and a session is cached locally.
45
+ stml login
46
+ stml whoami
47
+
48
+ # 2. See your apps and their URLs (the handle you pull/push by).
49
+ stml list
50
+
51
+ # 3. Pull an app's source into a folder — identify it by its URL (copy from
52
+ # `stml list` or the browser; a trailing /flows/… or /sessions/… is fine).
53
+ stml pull https://app.stml.io/orgs/acme/ws/main/apps/my-app-a1b2 ./my-app
54
+
55
+ # 3. Edit with your own tools, commit to git, whatever.
56
+
57
+ # 4. Push it back — checks, writes the overlay, and deploys in one step.
58
+ stml push ./my-app # app remembered from pull
59
+ # or: stml push https://app.stml.io/orgs/acme/ws/main/apps/my-app-a1b2 ./my-app
60
+
61
+ # 5. When ready, publish a version to the App Store.
62
+ # (bump project.version in pyproject.toml first; set [tool.stml].issuer-org)
63
+ stml publish ./my-app
64
+ ```
65
+
66
+ ## Authentication
67
+
68
+ `stml login` runs the platform's browser OAuth flow (the same one the monorepo's
69
+ `npm run libs:login` uses): you authenticate in the browser, approve, and a
70
+ **refresh token** is cached under your OS config dir (`~/Library/Application
71
+ Support/stml`, `$XDG_CONFIG_HOME/stml`, or `%APPDATA%\stml`). Access tokens are
72
+ minted per-command and never written to disk.
73
+
74
+ `STML4_TOKEN`, if set, overrides the cache verbatim — how CI and the monorepo
75
+ (`STML4_TOKEN=$(npm run --silent token)`) authenticate.
76
+
77
+ ## Commands
78
+
79
+ | Command | What |
80
+ |---|---|
81
+ | `stml login` / `logout` / `whoami` | session auth |
82
+ | `stml list [--workspace <ws>]` | list your apps with their app URLs |
83
+ | `stml pull <app-url> [<dir>]` | pull overlay source to a folder (bare slug also works) |
84
+ | `stml push [<app-url>] <dir>` | push a folder to the overlay **and deploy** (`--no-deploy`, `--prune`) |
85
+ | `stml publish <dir> [--org <slug>]` | publish a library version from a folder |
86
+ | `stml init <dir> [--name <name>]` | scaffold a new library directory |
@@ -0,0 +1,76 @@
1
+ # stml — the partner CLI
2
+
3
+ Pull an app you built on the platform down to a local folder, edit it with your
4
+ own editor and git, push it back to test, and publish a finished version to the
5
+ App Store — without cloning the stml4 monorepo.
6
+
7
+ Spec: [`docs/user-stories/feature/024-app-authoring-platform.md`](../docs/user-stories/feature/024-app-authoring-platform.md).
8
+
9
+ ## Requirements
10
+
11
+ - **Python 3.11 or newer** on your PATH. That's the only hard requirement.
12
+ - macOS: `brew install python@3.12` (or python.org). **Not** the system
13
+ `/usr/bin/python3` — it's often 3.9, which is too old.
14
+ - Linux: your distro's Python 3.11+, or `pipx`/`pyenv`/`uv`.
15
+ - Windows 10/11: the python.org installer (gives you the `py` launcher).
16
+
17
+ ## Install
18
+
19
+ | Method | Command |
20
+ |---|---|
21
+ | **pipx** (recommended) | `pipx install stml-cli` |
22
+ | pip (in a venv) | `pip install stml-cli` |
23
+ | single file (no installer) | `curl -fsSL <backend>/cli/stml -o stml && chmod +x stml` |
24
+
25
+ Runs on **macOS, Linux, and Windows** — it's pure-Python and touches nothing
26
+ platform-specific beyond the filesystem and HTTPS.
27
+
28
+ ## Quickstart
29
+
30
+ ```sh
31
+ # Point at your backend (default: http://localhost:8010)
32
+ export STML4_BACKEND=https://api.stml.io
33
+
34
+ # 1. Sign in — opens your browser; approve, and a session is cached locally.
35
+ stml login
36
+ stml whoami
37
+
38
+ # 2. See your apps and their URLs (the handle you pull/push by).
39
+ stml list
40
+
41
+ # 3. Pull an app's source into a folder — identify it by its URL (copy from
42
+ # `stml list` or the browser; a trailing /flows/… or /sessions/… is fine).
43
+ stml pull https://app.stml.io/orgs/acme/ws/main/apps/my-app-a1b2 ./my-app
44
+
45
+ # 3. Edit with your own tools, commit to git, whatever.
46
+
47
+ # 4. Push it back — checks, writes the overlay, and deploys in one step.
48
+ stml push ./my-app # app remembered from pull
49
+ # or: stml push https://app.stml.io/orgs/acme/ws/main/apps/my-app-a1b2 ./my-app
50
+
51
+ # 5. When ready, publish a version to the App Store.
52
+ # (bump project.version in pyproject.toml first; set [tool.stml].issuer-org)
53
+ stml publish ./my-app
54
+ ```
55
+
56
+ ## Authentication
57
+
58
+ `stml login` runs the platform's browser OAuth flow (the same one the monorepo's
59
+ `npm run libs:login` uses): you authenticate in the browser, approve, and a
60
+ **refresh token** is cached under your OS config dir (`~/Library/Application
61
+ Support/stml`, `$XDG_CONFIG_HOME/stml`, or `%APPDATA%\stml`). Access tokens are
62
+ minted per-command and never written to disk.
63
+
64
+ `STML4_TOKEN`, if set, overrides the cache verbatim — how CI and the monorepo
65
+ (`STML4_TOKEN=$(npm run --silent token)`) authenticate.
66
+
67
+ ## Commands
68
+
69
+ | Command | What |
70
+ |---|---|
71
+ | `stml login` / `logout` / `whoami` | session auth |
72
+ | `stml list [--workspace <ws>]` | list your apps with their app URLs |
73
+ | `stml pull <app-url> [<dir>]` | pull overlay source to a folder (bare slug also works) |
74
+ | `stml push [<app-url>] <dir>` | push a folder to the overlay **and deploy** (`--no-deploy`, `--prune`) |
75
+ | `stml publish <dir> [--org <slug>]` | publish a library version from a folder |
76
+ | `stml init <dir> [--name <name>]` | scaffold a new library directory |
@@ -0,0 +1,24 @@
1
+ [project]
2
+ name = "stml-cli"
3
+ version = "0.1.0"
4
+ description = "The stml partner CLI — pull, edit, push, and publish stml apps and libraries."
5
+ readme = "README.md"
6
+ requires-python = ">=3.11"
7
+ authors = [{ name = "stml" }]
8
+ # Runtime is stdlib-only; certifi is the sole dependency, for TLS trust on
9
+ # framework Python builds that ship no CA bundle (024 §Distributing the partner CLI).
10
+ dependencies = ["certifi"]
11
+
12
+ [project.scripts]
13
+ stml = "stml_cli.__main__:main"
14
+
15
+ [project.urls]
16
+ Homepage = "https://stml.io"
17
+
18
+ [build-system]
19
+ requires = ["setuptools>=68"]
20
+ build-backend = "setuptools.build_meta"
21
+
22
+ [tool.setuptools.packages.find]
23
+ where = ["."]
24
+ include = ["stml_cli*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,22 @@
1
+ """stml — the partner CLI (024 §Distributing the partner CLI).
2
+
3
+ Pure-Python, stdlib only. ``certifi`` is the sole packaging dependency, used to
4
+ point OpenSSL at a CA bundle so HTTPS to a real backend verifies on framework
5
+ Python builds that ship no CA store (same fix as the monorepo publish scripts).
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import os
11
+
12
+ __version__ = "0.1.0"
13
+
14
+ # TLS trust: framework/python.org builds have no OpenSSL CA path, so a real
15
+ # https backend fails with "unable to get local issuer certificate". certifi's
16
+ # bundle fixes it. setdefault honours an operator override; no-op for http.
17
+ try: # pragma: no cover - environment dependent
18
+ import certifi
19
+
20
+ os.environ.setdefault("SSL_CERT_FILE", certifi.where())
21
+ except ImportError: # pragma: no cover
22
+ pass
@@ -0,0 +1,153 @@
1
+ """`stml` CLI entry point — argparse dispatch (024 §CLI surface).
2
+
3
+ stml login | logout | whoami
4
+ stml list [--workspace <ws>]
5
+ stml pull <app-url> [<dir>]
6
+ stml push [<app-url>] <dir>
7
+ stml detach <app-url> (fork: own the full source)
8
+ stml revert <app-url> [--yes] (reset: back to the clean library)
9
+ stml status <app-url>
10
+ stml publish <dir> [--org <slug>]
11
+ stml init <dir> [--name <name>]
12
+
13
+ Auth: STML4_TOKEN overrides everything; otherwise the cached `stml login`
14
+ session is used (and silently refreshed). Backend: --backend or $STML4_BACKEND.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import argparse
20
+ import sys
21
+
22
+ from . import __version__, commands, http
23
+ from .config import DEFAULT_BACKEND
24
+
25
+
26
+ def build_parser() -> argparse.ArgumentParser:
27
+ p = argparse.ArgumentParser(prog="stml", description="The stml partner CLI.")
28
+ p.add_argument("--version", action="version", version=f"stml {__version__}")
29
+ p.add_argument("--backend", default=DEFAULT_BACKEND, help=f"Backend URL (default: {DEFAULT_BACKEND})")
30
+ sub = p.add_subparsers(dest="cmd", required=True)
31
+
32
+ sub.add_parser("login", help="Authenticate in the browser and cache a session").set_defaults(func=commands.cmd_login)
33
+ sub.add_parser("logout", help="Delete the cached session").set_defaults(func=commands.cmd_logout)
34
+ sub.add_parser("whoami", help="Show the resolved user + backend").set_defaults(func=commands.cmd_whoami)
35
+
36
+ ls = sub.add_parser(
37
+ "list",
38
+ help="List apps you can access (name + pull handle)",
39
+ description="List apps you can access, with the exact `stml pull` handle for each.",
40
+ )
41
+ ls.add_argument("--workspace", metavar="<workspace>", help="Filter to one workspace (slug or name)")
42
+ ls.set_defaults(func=commands.cmd_list)
43
+
44
+ # pull / push (an app's source)
45
+ pull = sub.add_parser(
46
+ "pull",
47
+ help="Pull an app's source to a local folder",
48
+ description="Pull an app's source to a local folder.",
49
+ epilog=(
50
+ "Identify the app by its URL — copy it from `stml list` or straight from\n"
51
+ "the browser. A trailing /flows/…, /sessions/… or /settings is ignored, so\n"
52
+ "the URL of a flow or a run resolves to its app just fine.\n\n"
53
+ "examples:\n"
54
+ " stml pull https://app.stml.io/orgs/acme/ws/main/apps/invoice-sync-a1b2\n"
55
+ " stml pull https://app.stml.io/orgs/acme/ws/main/apps/invoice-sync-a1b2/flows/x ./work\n"
56
+ " stml pull invoice-sync-a1b2 # a bare slug also works"
57
+ ),
58
+ formatter_class=argparse.RawDescriptionHelpFormatter,
59
+ )
60
+ pull.add_argument("app", metavar="<app-url>", help="App URL (from `stml list` or the browser); a bare slug also works")
61
+ pull.add_argument("dir", nargs="?", metavar="<dir>", help="Target folder (default: ./<app-slug>)")
62
+ pull.set_defaults(func=commands.cmd_pull)
63
+
64
+ push = sub.add_parser(
65
+ "push",
66
+ help="Push a local folder into the app overlay and deploy",
67
+ description="Push a local folder into the app's overlay and deploy it.",
68
+ epilog=(
69
+ "The <app-url> is optional when pushing a folder created by `stml pull`\n"
70
+ "(it remembers the app in .stml/app.json).\n\n"
71
+ "examples:\n"
72
+ " stml push ./invoice-sync # remembered from pull\n"
73
+ " stml push https://app.stml.io/orgs/acme/ws/main/apps/invoice-sync-a1b2 ./invoice-sync\n"
74
+ " stml push ./invoice-sync --no-deploy"
75
+ ),
76
+ formatter_class=argparse.RawDescriptionHelpFormatter,
77
+ )
78
+ push.add_argument("app", nargs="?", metavar="<app-url>", help="App URL to push to (optional for a pulled folder)")
79
+ push.add_argument("dir", metavar="<dir>", help="Source folder to push")
80
+ push.add_argument("--no-deploy", action="store_true", help="Write the overlay without deploying")
81
+ push.add_argument("--prune", action="store_true", help="Delete overlay paths absent from the folder")
82
+ push.set_defaults(func=commands.cmd_push)
83
+
84
+ # detach / revert / status (story 035 — the app's attachment lifecycle)
85
+ detach = sub.add_parser(
86
+ "detach",
87
+ help="Fork an installed app: full source into the overlay, library pin cleared",
88
+ description=(
89
+ "Fork an installed app into a self-contained one. The full effective source "
90
+ "(base library + your overlay) is materialised into the app's overlay and the "
91
+ "library pin is cleared — after this, `stml pull` returns the whole tree and "
92
+ "`stml publish` can ship it as your own standalone library. The app stops "
93
+ "receiving library updates. Non-destructive; `stml revert` undoes it."
94
+ ),
95
+ )
96
+ detach.add_argument("app", metavar="<app-url>", help="App URL (from `stml list`); a bare slug also works")
97
+ detach.set_defaults(func=commands.cmd_detach)
98
+
99
+ revert = sub.add_parser(
100
+ "revert",
101
+ help="DESTRUCTIVE: discard all customisations, restore the clean library version",
102
+ description=(
103
+ "Permanently delete every overlay customisation (code AND page edits) and restore "
104
+ "the app to its clean library version. A detached app is re-attached to the exact "
105
+ "version it was forked from. Connections, configurations, triggers and run history "
106
+ "are preserved. THERE IS NO UNDO."
107
+ ),
108
+ )
109
+ revert.add_argument("app", metavar="<app-url>", help="App URL (from `stml list`); a bare slug also works")
110
+ revert.add_argument("--yes", action="store_true", help="Skip the interactive confirmation")
111
+ revert.set_defaults(func=commands.cmd_revert)
112
+
113
+ status = sub.add_parser(
114
+ "status",
115
+ help="Show an app's mode (attached / detached / scratch) and provenance",
116
+ )
117
+ status.add_argument("app", metavar="<app-url>", help="App URL (from `stml list`); a bare slug also works")
118
+ status.set_defaults(func=commands.cmd_status)
119
+
120
+ # publish / init (library)
121
+ publish = sub.add_parser("publish", help="Publish a library version from a local folder")
122
+ publish.add_argument("dir")
123
+ publish.add_argument("--org", help="Issuer org slug (overrides [tool.stml].issuer-org)")
124
+ publish.set_defaults(func=commands.cmd_publish)
125
+
126
+ init = sub.add_parser("init", help="Scaffold a new library directory")
127
+ init.add_argument("dir")
128
+ init.add_argument("--name")
129
+ init.set_defaults(func=commands.cmd_init)
130
+
131
+ return p
132
+
133
+
134
+ def main(argv: list[str] | None = None) -> int:
135
+ args = build_parser().parse_args(argv)
136
+ try:
137
+ args.func(args)
138
+ except http.HttpError as e:
139
+ # An unhandled server/transport error (handled ones — e.g. a 409 the
140
+ # pull picker catches — never reach here). Print the clean detail.
141
+ print(f"✗ {e.detail}", file=sys.stderr)
142
+ return 1
143
+ except SystemExit as e:
144
+ # SystemExit carries our user-facing error strings; print and exit non-zero.
145
+ if isinstance(e.code, str):
146
+ print(e.code, file=sys.stderr)
147
+ return 1
148
+ return e.code or 0
149
+ return 0
150
+
151
+
152
+ if __name__ == "__main__":
153
+ raise SystemExit(main())