shortcutkit 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 +21 -0
- package/README.md +190 -0
- package/dist/cli.d.ts +1 -0
- package/dist/definitions.d.ts +69 -0
- package/dist/generated/actions.d.ts +5643 -0
- package/dist/index.d.ts +92 -0
- package/dist/index.js +19912 -0
- package/dist/plist.d.ts +4 -0
- package/dist/provenance.d.ts +21 -0
- package/dist/values.d.ts +59 -0
- package/package.json +52 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 frontboat
|
|
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
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# shortcutkit
|
|
2
|
+
|
|
3
|
+
Build, validate and sign Apple Shortcuts (`.shortcut`) files from TypeScript or Python.
|
|
4
|
+
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://github.com/frontboat/shortcutkit/actions)
|
|
7
|
+
|
|
8
|
+
## About
|
|
9
|
+
|
|
10
|
+
Shortcuts has no public file-format specification and no public list of what its actions
|
|
11
|
+
accept. shortcutkit fills that gap by extracting the action definitions and value encodings
|
|
12
|
+
from WorkflowKit, the engine inside the Shortcuts app, and generating a typed catalogue from
|
|
13
|
+
them. Every one of the 339 built-in actions is available with its parameter keys and value
|
|
14
|
+
shapes checked at compile time in TypeScript, or at run time in Python. Nothing was
|
|
15
|
+
transcribed by hand, and `data/provenance.json` records exactly which macOS and Shortcuts build
|
|
16
|
+
the data came from.
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
- **Every built-in action, typed.** `actions.*` lists all 339 identifiers, with each action's
|
|
21
|
+
name, description, summary, output and parameter keys as hover documentation.
|
|
22
|
+
- **Parameters checked before you run anything.** `{ WFStoredContentGlobalValue: "yes" }` is a
|
|
23
|
+
compile error; a switch takes a boolean or a reference. Enumeration choices are suggested.
|
|
24
|
+
Any plain value slot also accepts an attachment, because that is how Shortcuts works.
|
|
25
|
+
- **Value helpers that match the engine's serialization.** `ref()` to another action's output,
|
|
26
|
+
`variable()`, `shortcutInput()`, `clipboard()`, `currentDate()`, `ask()`, `text()` for
|
|
27
|
+
strings with embedded references, and `picker()` for variable-picker parameters.
|
|
28
|
+
- **Control flow.** `if()` / `otherwise()` / `endIf()` and `repeatEach()` / `endRepeatEach()`
|
|
29
|
+
manage the grouping identifiers for you.
|
|
30
|
+
- **App Intents from installed apps.** Any identifier outside the built-in set is accepted;
|
|
31
|
+
built-in identifiers and their parameter keys are also validated at run time.
|
|
32
|
+
- **Definitions and provenance.** `getDefinition(id)` returns the full record (icon, keywords,
|
|
33
|
+
parameter classes, required resources); `provenance` says which build produced the data.
|
|
34
|
+
- **Signing.** `Shortcut.sign()` wraps `shortcuts sign` so the output imports on any device.
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install shortcutkit # or: bun add shortcutkit
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Python:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install shortcutkit
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Requirements
|
|
49
|
+
|
|
50
|
+
- Node 20 or newer, or Bun, for the TypeScript package. The published package is plain ES
|
|
51
|
+
module JavaScript with type declarations. Python 3.9 or newer for the Python package.
|
|
52
|
+
- A Mac signed into iCloud for `sign()`. It runs `shortcuts sign`, which refuses to work
|
|
53
|
+
without an iCloud login even in `anyone` mode, so it cannot run on CI runners. `write()`
|
|
54
|
+
converts the plist to binary with `plutil` when present and otherwise leaves the XML form.
|
|
55
|
+
Building, validating and writing the unsigned file works anywhere.
|
|
56
|
+
|
|
57
|
+
## Usage
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { Shortcut, actions, ref, text } from "shortcutkit";
|
|
61
|
+
|
|
62
|
+
const s = new Shortcut("Greeting", { color: "Teal" });
|
|
63
|
+
const got = s.action(actions.getstoredcontent, { WFStoredContentKey: "greeting" });
|
|
64
|
+
s.action(actions.showresult, { Text: text("Stored: ", ref(got)) });
|
|
65
|
+
await s.write(); // Greeting.shortcut
|
|
66
|
+
Shortcut.sign("Greeting.shortcut", "Greeting-signed.shortcut");
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Open the signed file and Shortcuts imports it.
|
|
70
|
+
|
|
71
|
+
### Control flow
|
|
72
|
+
|
|
73
|
+
`if()` returns a grouping identifier that the matching `otherwise()` and `endIf()` calls take
|
|
74
|
+
back:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
const gid = s.if(ref(got), "has_any_value");
|
|
78
|
+
s.action(actions.showresult, { Text: text("Stored value: ", ref(got)) });
|
|
79
|
+
s.otherwise(gid);
|
|
80
|
+
s.action(actions.showresult, { Text: text("Nothing stored") });
|
|
81
|
+
s.endIf(gid);
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Actions from other apps
|
|
85
|
+
|
|
86
|
+
App Intents actions are not in the catalogue, so pass the identifier as a string. Parameter
|
|
87
|
+
keys are then typed as `Record<string, Value>` and passed through as given:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
s.action("com.example.app.CreateNote", { title: text("Hello"), body: ref(got) });
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Python
|
|
94
|
+
|
|
95
|
+
Same API and same bundled data, with snake_case names and run-time value checks instead of
|
|
96
|
+
compile-time ones:
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
from shortcutkit import Shortcut, actions, ref, text
|
|
100
|
+
|
|
101
|
+
s = Shortcut("Greeting", color="Teal")
|
|
102
|
+
got = s.action(actions.GETSTOREDCONTENT, WFStoredContentKey="greeting")
|
|
103
|
+
s.action(actions.SHOWRESULT, Text=text("Stored: ", ref(got)))
|
|
104
|
+
s.write()
|
|
105
|
+
Shortcut.sign("Greeting.shortcut", "Greeting-signed.shortcut")
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
See [`python/README.md`](python/README.md) for the full Python surface.
|
|
109
|
+
|
|
110
|
+
### Demo
|
|
111
|
+
|
|
112
|
+
Both packages ship a demo that builds a working shortcut with storage, an If/Otherwise block
|
|
113
|
+
and output:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
bun run demo out.shortcut
|
|
117
|
+
python -m shortcutkit demo out.shortcut
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Documentation
|
|
121
|
+
|
|
122
|
+
- [`docs/shortcut-file-format.md`](docs/shortcut-file-format.md): the `.shortcut` format end
|
|
123
|
+
to end, every field.
|
|
124
|
+
- [`docs/builtin-actions-reference.md`](docs/builtin-actions-reference.md): all 339 built-in
|
|
125
|
+
actions with their parameters.
|
|
126
|
+
- [`docs/parameter-encodings.md`](docs/parameter-encodings.md): how each parameter class is
|
|
127
|
+
serialized.
|
|
128
|
+
- [`docs/extraction.md`](docs/extraction.md): how the data was obtained, what did not work,
|
|
129
|
+
and what remains approximate. A handful of condition codes rest on community documentation.
|
|
130
|
+
|
|
131
|
+
## Development
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
git clone https://github.com/frontboat/shortcutkit.git && cd shortcutkit
|
|
135
|
+
bun install
|
|
136
|
+
bun test # all tests
|
|
137
|
+
bun run typecheck # tsc --noEmit; also verifies the @ts-expect-error assertions in tests
|
|
138
|
+
bun run build # bundles src/ into dist/index.js for Node and emits the .d.ts files
|
|
139
|
+
|
|
140
|
+
cd python && python3 -m venv .venv && .venv/bin/pip install -e .
|
|
141
|
+
.venv/bin/python -m shortcutkit demo /tmp/Demo.shortcut
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
CI runs the type check, the tests, a Node import of the built package, a Python install and an
|
|
145
|
+
unsigned build of the demo shortcut on macOS. Signing is not exercised in CI because the runner
|
|
146
|
+
has no iCloud login.
|
|
147
|
+
|
|
148
|
+
### Repository layout
|
|
149
|
+
|
|
150
|
+
| Path | Contents |
|
|
151
|
+
|---|---|
|
|
152
|
+
| `src/` | The TypeScript package source. `src/generated/actions.ts` is produced by the tools. `bun run build` bundles it into `dist/`, which is what npm ships. |
|
|
153
|
+
| `python/` | The Python package. `actions.py` and `data/` are produced by the tools. |
|
|
154
|
+
| `data/` | Everything extracted from the engine: definitions, parameter encodings, serialization table, and `provenance.json`. |
|
|
155
|
+
| `docs/` | The format reference, action reference, encodings reference and extraction notes. |
|
|
156
|
+
| `tools/` | The extraction pipeline. |
|
|
157
|
+
|
|
158
|
+
Never hand-edit the generated files. Change the generator or the data and re-run the pipeline.
|
|
159
|
+
|
|
160
|
+
### Regenerating the data
|
|
161
|
+
|
|
162
|
+
The extracted data is committed on purpose. It can only be produced on a Mac, through private
|
|
163
|
+
API that changes between releases, so committing it is what makes the package reproducible.
|
|
164
|
+
To refresh after a macOS update, on a Mac with Xcode or the Command Line Tools:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
bun run extract # ~1 minute: loads WorkflowKit, dumps and serializes everything
|
|
168
|
+
bun install && bun test
|
|
169
|
+
bun run diff-data # what changed vs the committed data, and the semver bump it implies
|
|
170
|
+
bun run changelog # the same as a CHANGELOG.md section
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Both packages and the data are versioned together. A refresh that only adds actions or
|
|
174
|
+
parameters is a minor release. One that removes or retypes any is a major release, because the
|
|
175
|
+
generated catalogue is part of the type surface. Bump `package.json` and
|
|
176
|
+
`python/pyproject.toml` together and add the `bun run changelog` output to `CHANGELOG.md`.
|
|
177
|
+
|
|
178
|
+
The tools also dump Apple's gallery workflows, installed apps' App Intents actions and the raw
|
|
179
|
+
identifier list. Those files are local and gitignored.
|
|
180
|
+
|
|
181
|
+
## Status
|
|
182
|
+
|
|
183
|
+
Data extracted on macOS 27.0 with Shortcuts 10.0 (see `data/provenance.json`). Generated files
|
|
184
|
+
declare minimum client version 900, which any current Shortcuts accepts. The private classes
|
|
185
|
+
the tools call can change with any release; the package itself depends only on the committed
|
|
186
|
+
data. Generated shortcuts import and run.
|
|
187
|
+
|
|
188
|
+
## License
|
|
189
|
+
|
|
190
|
+
shortcutkit is licensed under the MIT license. See [`LICENSE`](LICENSE) for details.
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function demo(out: string): Promise<void>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { ActionId } from "./generated/actions.js";
|
|
2
|
+
/** A localized string as the dumper resolves it: the English default. Parameter summaries keep their format string. */
|
|
3
|
+
export type Localized = string | {
|
|
4
|
+
format?: string;
|
|
5
|
+
title?: string;
|
|
6
|
+
possibleValues?: ParameterSummaryValue[];
|
|
7
|
+
};
|
|
8
|
+
export interface ParameterSummaryValue {
|
|
9
|
+
key?: string;
|
|
10
|
+
format?: string;
|
|
11
|
+
requiredValues?: Record<string, unknown>;
|
|
12
|
+
}
|
|
13
|
+
export interface ParameterDefinition {
|
|
14
|
+
Key: string;
|
|
15
|
+
/** WorkflowKit parameter class, e.g. "WFSwitchParameter"; decides the value encoding (see values.ts). */
|
|
16
|
+
Class: string;
|
|
17
|
+
Label?: string;
|
|
18
|
+
Placeholder?: string;
|
|
19
|
+
Description?: string;
|
|
20
|
+
DefaultValue?: unknown;
|
|
21
|
+
/** Choice labels for enumeration parameters. */
|
|
22
|
+
Items?: string[];
|
|
23
|
+
Hidden?: boolean;
|
|
24
|
+
RequiredResources?: unknown[];
|
|
25
|
+
[key: string]: unknown;
|
|
26
|
+
}
|
|
27
|
+
export interface ActionDefinition {
|
|
28
|
+
ActionClass?: string;
|
|
29
|
+
Name?: string;
|
|
30
|
+
Description?: {
|
|
31
|
+
DescriptionSummary?: string;
|
|
32
|
+
DescriptionInput?: string;
|
|
33
|
+
DescriptionResult?: string;
|
|
34
|
+
DescriptionNote?: string;
|
|
35
|
+
};
|
|
36
|
+
ActionKeywords?: string;
|
|
37
|
+
IconSymbol?: string;
|
|
38
|
+
IconColor?: string;
|
|
39
|
+
Parameters?: ParameterDefinition[];
|
|
40
|
+
ParameterSummary?: Localized;
|
|
41
|
+
Input?: {
|
|
42
|
+
ParameterKey?: string;
|
|
43
|
+
Types?: string[];
|
|
44
|
+
Required?: boolean;
|
|
45
|
+
Multiple?: boolean;
|
|
46
|
+
InputTypeDeterminesOutputType?: boolean;
|
|
47
|
+
};
|
|
48
|
+
Output?: {
|
|
49
|
+
OutputName?: string;
|
|
50
|
+
Types?: string[];
|
|
51
|
+
Multiple?: boolean;
|
|
52
|
+
DisclosureLevel?: string;
|
|
53
|
+
};
|
|
54
|
+
InputPassthrough?: boolean;
|
|
55
|
+
ResidentCompatible?: boolean;
|
|
56
|
+
RequiresUserInteraction?: boolean;
|
|
57
|
+
RequiredResources?: Array<string | {
|
|
58
|
+
resource: string;
|
|
59
|
+
[key: string]: unknown;
|
|
60
|
+
}>;
|
|
61
|
+
FillingProvider?: string;
|
|
62
|
+
AppIdentifier?: string;
|
|
63
|
+
[key: string]: unknown;
|
|
64
|
+
}
|
|
65
|
+
/** The full definition record for a built-in action, or undefined for identifiers the engine does not know. */
|
|
66
|
+
export declare function getDefinition(identifier: ActionId): ActionDefinition;
|
|
67
|
+
export declare function getDefinition(identifier: string): ActionDefinition | undefined;
|
|
68
|
+
/** Every built-in definition, keyed by identifier. */
|
|
69
|
+
export declare function allDefinitions(): Readonly<Record<string, ActionDefinition>>;
|