@lightward/mechanic-cli 0.1.1 → 0.1.4
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/README.md +75 -37
- package/dist/base-command.d.ts +1 -0
- package/dist/base-command.d.ts.map +1 -1
- package/dist/base-command.js +16 -1
- package/dist/client.d.ts +3 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +4 -1
- package/dist/commands/auth/login.d.ts.map +1 -1
- package/dist/commands/auth/login.js +6 -2
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +9 -7
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +12 -6
- package/dist/commands/tasks/bundle.d.ts.map +1 -1
- package/dist/commands/tasks/bundle.js +37 -5
- package/dist/commands/tasks/diff.d.ts.map +1 -1
- package/dist/commands/tasks/diff.js +2 -2
- package/dist/commands/tasks/new.d.ts +16 -0
- package/dist/commands/tasks/new.d.ts.map +1 -0
- package/dist/commands/tasks/new.js +104 -0
- package/dist/commands/tasks/open.d.ts.map +1 -1
- package/dist/commands/tasks/open.js +2 -2
- package/dist/commands/tasks/preview.d.ts +4 -0
- package/dist/commands/tasks/preview.d.ts.map +1 -1
- package/dist/commands/tasks/preview.js +43 -3
- package/dist/commands/tasks/publish.js +1 -1
- package/dist/commands/tasks/pull.d.ts.map +1 -1
- package/dist/commands/tasks/pull.js +8 -5
- package/dist/commands/tasks/push.d.ts +2 -0
- package/dist/commands/tasks/push.d.ts.map +1 -1
- package/dist/commands/tasks/push.js +22 -7
- package/dist/commands/tasks/status.d.ts +1 -1
- package/dist/commands/tasks/status.d.ts.map +1 -1
- package/dist/commands/tasks/status.js +26 -14
- package/dist/commands/tasks/unbundle.d.ts.map +1 -1
- package/dist/commands/tasks/unbundle.js +5 -5
- package/dist/commands/tasks/validate.d.ts +1 -0
- package/dist/commands/tasks/validate.d.ts.map +1 -1
- package/dist/commands/tasks/validate.js +40 -18
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +19 -11
- package/dist/tasks.js +2 -2
- package/dist/types.d.ts +12 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/update-check.d.ts +14 -0
- package/dist/update-check.d.ts.map +1 -0
- package/dist/update-check.js +136 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -38,6 +38,8 @@ Install from npm:
|
|
|
38
38
|
npm install -g @lightward/mechanic-cli
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
+
In interactive terminals, Mechanic occasionally checks npm and prints the update command when a newer CLI is available.
|
|
42
|
+
|
|
41
43
|
## Quick Start
|
|
42
44
|
|
|
43
45
|
Create an API token in Mechanic:
|
|
@@ -60,20 +62,30 @@ Pull existing Mechanic tasks into local JSON files:
|
|
|
60
62
|
mechanic tasks pull
|
|
61
63
|
```
|
|
62
64
|
|
|
65
|
+
To start from scratch instead, create a new blank local task. This writes a
|
|
66
|
+
starter JSON file and matching helper directory. You can use this in any
|
|
67
|
+
initialized CLI project; it does not require a fresh repository:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
mechanic tasks new order-tagger
|
|
71
|
+
```
|
|
72
|
+
|
|
63
73
|
For most Liquid or documentation edits, unbundle one task into helper files,
|
|
64
74
|
edit those files, then bundle the helper directory back into its canonical JSON
|
|
65
75
|
file. The JSON file remains the deployable source of truth; the helper directory
|
|
66
|
-
is the comfortable editing view beside it.
|
|
76
|
+
is the comfortable editing view beside it. Tasks created with `tasks new`
|
|
77
|
+
already have both files, so you can start editing the helper directory
|
|
78
|
+
immediately.
|
|
67
79
|
|
|
68
80
|
```bash
|
|
69
|
-
mechanic tasks unbundle
|
|
81
|
+
mechanic tasks unbundle order-tagger
|
|
70
82
|
# edit tasks/order-tagger/script.liquid, docs.md, or task.json
|
|
71
|
-
mechanic tasks bundle
|
|
83
|
+
mechanic tasks bundle order-tagger
|
|
72
84
|
mechanic tasks status
|
|
73
|
-
mechanic tasks preview
|
|
74
|
-
mechanic tasks diff
|
|
75
|
-
mechanic tasks publish
|
|
76
|
-
mechanic tasks publish
|
|
85
|
+
mechanic tasks preview order-tagger
|
|
86
|
+
mechanic tasks diff order-tagger
|
|
87
|
+
mechanic tasks publish order-tagger --dry-run
|
|
88
|
+
mechanic tasks publish order-tagger
|
|
77
89
|
```
|
|
78
90
|
|
|
79
91
|
For normal setup, paste the token into the masked prompt or run
|
|
@@ -102,8 +114,9 @@ mechanic auth logout
|
|
|
102
114
|
mechanic github init [--force]
|
|
103
115
|
mechanic shop status [--json]
|
|
104
116
|
mechanic tasks list [--verbose] [--json]
|
|
117
|
+
mechanic tasks new <name> [--force] [--json]
|
|
105
118
|
mechanic tasks open <task>
|
|
106
|
-
mechanic tasks status [task] [--
|
|
119
|
+
mechanic tasks status [task] [--local] [--json]
|
|
107
120
|
mechanic tasks pull [--force]
|
|
108
121
|
mechanic tasks pull <task> [--force]
|
|
109
122
|
mechanic tasks pull --all [--force]
|
|
@@ -113,38 +126,61 @@ mechanic tasks diff --all [--exit-code] [--json]
|
|
|
113
126
|
mechanic tasks publish <task> [--force] [--dry-run] [--json]
|
|
114
127
|
mechanic tasks publish --all [--force] [--dry-run] [--json]
|
|
115
128
|
mechanic tasks unbundle <task> [--out <dir>] [--json]
|
|
116
|
-
mechanic tasks bundle <dir|file> [--out <file>] [--json]
|
|
117
|
-
mechanic tasks validate <
|
|
129
|
+
mechanic tasks bundle <task|dir|file> [--out <file>] [--json]
|
|
130
|
+
mechanic tasks validate <task|dir> [--json]
|
|
118
131
|
```
|
|
119
132
|
|
|
120
|
-
Most task commands accept a `<task>` selector.
|
|
121
|
-
|
|
122
|
-
|
|
133
|
+
Most task commands accept a `<task>` selector. In day-to-day use, prefer the
|
|
134
|
+
short local task slug:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
mechanic tasks preview order-tagger
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
The CLI also accepts a full task JSON path, matching helper directory, or linked
|
|
141
|
+
remote task ID:
|
|
123
142
|
|
|
124
143
|
```bash
|
|
125
144
|
mechanic tasks preview tasks/order-tagger.json
|
|
126
145
|
mechanic tasks preview tasks/order-tagger
|
|
127
|
-
mechanic tasks preview order-tagger
|
|
128
146
|
mechanic tasks preview 171578bf-79e2-46af-857a-dbd71c6b7b2b
|
|
129
147
|
```
|
|
130
148
|
|
|
131
149
|
The local file is the working copy. The remote task ID is the Mechanic app's
|
|
132
150
|
address for that task. `tasks list` shows linked local files when the CLI knows
|
|
133
|
-
them. If a short
|
|
134
|
-
full file path.
|
|
151
|
+
them. If a short slug matches more than one local file, the CLI asks for the
|
|
152
|
+
full file path; that is the main reason to use `tasks/<name>.json` directly.
|
|
153
|
+
|
|
154
|
+
Task names and local file names are related, but they are not the same identity.
|
|
155
|
+
When the CLI first pulls or creates a task, it uses the task name to choose a
|
|
156
|
+
readable local slug like `order-tagger`, which becomes `tasks/order-tagger.json`.
|
|
157
|
+
After that, the remote task ID stored in `.mechanic/links.json` is the sync
|
|
158
|
+
identity. If you rename the task in Mechanic, the next pull keeps the existing
|
|
159
|
+
local file name and updates the `name` field inside the JSON. If you rename the
|
|
160
|
+
local JSON file or helper directory by hand, the CLI treats that as a new local
|
|
161
|
+
slug and the task may appear unlinked. Keep local filenames stable unless you
|
|
162
|
+
also intentionally update `.mechanic/links.json` and verify with `tasks status`
|
|
163
|
+
and `tasks publish --dry-run`.
|
|
135
164
|
|
|
136
165
|
`tasks pull` pulls every task when you run it without arguments. Pass a task
|
|
137
166
|
selector when you only want one task. `tasks diff` and `tasks publish` operate
|
|
138
167
|
on one task when you pass a selector. They operate on every task only when you
|
|
139
168
|
explicitly pass `--all`.
|
|
140
169
|
|
|
170
|
+
`tasks new` creates a new blank local starter task. It writes both
|
|
171
|
+
`tasks/<slug>.json` and `tasks/<slug>/`, so you can edit the helper files first
|
|
172
|
+
and bundle them into the JSON file before publishing. It can be used any time in
|
|
173
|
+
an initialized CLI project, and it refuses to overwrite existing local task files
|
|
174
|
+
unless you pass `--force`. It does not create anything in Mechanic until you run
|
|
175
|
+
`tasks publish`; new published tasks are created disabled.
|
|
176
|
+
|
|
141
177
|
`shop status` shows the current Mechanic run queue for the configured shop:
|
|
142
178
|
running runs, waiting runs, queue lag, and the largest backlog groups by task,
|
|
143
179
|
action, and event topic.
|
|
144
180
|
|
|
145
|
-
`tasks status` shows whether local task JSON files are linked to
|
|
146
|
-
|
|
147
|
-
|
|
181
|
+
`tasks status` shows whether local task JSON files are linked, ready to publish,
|
|
182
|
+
and in sync with their remote Mechanic tasks. Use `--local` when you only want
|
|
183
|
+
the fast offline check for JSON validity, link state, and helper-folder drift.
|
|
148
184
|
Add `--json` when an editor integration needs task readiness, link state, and
|
|
149
185
|
remote sync state without parsing table output.
|
|
150
186
|
|
|
@@ -165,21 +201,22 @@ or enabling the task.
|
|
|
165
201
|
|
|
166
202
|
`tasks publish` sends local task JSON back to Mechanic.
|
|
167
203
|
|
|
168
|
-
`tasks publish --dry-run`
|
|
169
|
-
helper
|
|
170
|
-
existing remote
|
|
171
|
-
unchanged, or conflict. It does not write to
|
|
172
|
-
or local task JSON. New tasks created by
|
|
173
|
-
review and enable them in Mechanic when
|
|
204
|
+
`tasks publish --dry-run` is a publish preflight. It checks whether publishing
|
|
205
|
+
would be safe, including helper-folder drift, linked remote task changes, and
|
|
206
|
+
unlinked files that look like existing remote tasks. It then prints what would
|
|
207
|
+
create, update, stay unchanged, or stop as a conflict. It does not write to
|
|
208
|
+
Mechanic, `.mechanic/links.json`, or local task JSON. New tasks created by
|
|
209
|
+
`tasks publish` are created disabled; review and enable them in Mechanic when
|
|
210
|
+
they are ready to run.
|
|
174
211
|
|
|
175
212
|
For automation or editor integrations, add `--json` to `tasks list`, `tasks
|
|
176
213
|
status`, `tasks diff`, `tasks validate`, `tasks bundle`, `tasks unbundle`,
|
|
177
214
|
`tasks publish`, or `shop status`.
|
|
178
215
|
|
|
179
216
|
`tasks unbundle` and `tasks bundle` operate on one task at a time. By default,
|
|
180
|
-
`mechanic tasks unbundle
|
|
181
|
-
`tasks
|
|
182
|
-
|
|
217
|
+
`mechanic tasks unbundle order-tagger` writes to `tasks/order-tagger/`, and
|
|
218
|
+
`mechanic tasks bundle order-tagger` writes back to `tasks/order-tagger.json`.
|
|
219
|
+
You can pass the full JSON path when you want to be explicit.
|
|
183
220
|
|
|
184
221
|
When a task has `subscriptions_template`, the editable helper file is
|
|
185
222
|
`subscriptions.liquid`. Mechanic renders that template into `subscriptions`, so
|
|
@@ -207,8 +244,7 @@ open a task explicitly. Use `--app-url` or `MECHANIC_APP_URL` if your shop uses
|
|
|
207
244
|
a non-production Shopify app alias.
|
|
208
245
|
|
|
209
246
|
Use `mechanic tasks open <task>` to open a task in the Mechanic app from its
|
|
210
|
-
|
|
211
|
-
slug.
|
|
247
|
+
local task slug, linked local task JSON file, helper directory, or remote ID.
|
|
212
248
|
|
|
213
249
|
## Advanced: GitHub Actions Task Workflows
|
|
214
250
|
|
|
@@ -322,7 +358,7 @@ inside JSON is unpleasant. The helper workflow splits a task into separate
|
|
|
322
358
|
editable files:
|
|
323
359
|
|
|
324
360
|
```bash
|
|
325
|
-
mechanic tasks unbundle
|
|
361
|
+
mechanic tasks unbundle order-tagger --out order-tagger
|
|
326
362
|
```
|
|
327
363
|
|
|
328
364
|
`tasks unbundle` also accepts the same local task selectors as preview, diff,
|
|
@@ -330,6 +366,7 @@ and publish:
|
|
|
330
366
|
|
|
331
367
|
```bash
|
|
332
368
|
mechanic tasks unbundle order-tagger
|
|
369
|
+
mechanic tasks unbundle tasks/order-tagger.json
|
|
333
370
|
mechanic tasks unbundle tasks/order-tagger
|
|
334
371
|
mechanic tasks unbundle 171578bf-79e2-46af-857a-dbd71c6b7b2b
|
|
335
372
|
```
|
|
@@ -347,14 +384,15 @@ order-tagger/
|
|
|
347
384
|
After editing, bundle the helper directory back to canonical JSON:
|
|
348
385
|
|
|
349
386
|
```bash
|
|
350
|
-
mechanic tasks bundle
|
|
351
|
-
mechanic tasks validate
|
|
387
|
+
mechanic tasks bundle order-tagger
|
|
388
|
+
mechanic tasks validate order-tagger
|
|
352
389
|
```
|
|
353
390
|
|
|
354
|
-
`tasks bundle` accepts either side of the pair.
|
|
355
|
-
|
|
356
|
-
tasks/order-tagger
|
|
357
|
-
|
|
391
|
+
`tasks bundle` accepts the local task slug or either side of the pair.
|
|
392
|
+
`mechanic tasks bundle order-tagger` and `mechanic tasks bundle
|
|
393
|
+
tasks/order-tagger` both write `tasks/order-tagger.json`. `mechanic tasks bundle
|
|
394
|
+
tasks/order-tagger.json` reads `tasks/order-tagger/` and writes back to that
|
|
395
|
+
JSON file.
|
|
358
396
|
|
|
359
397
|
When unbundling, helper files are written only for string fields. `null` fields
|
|
360
398
|
stay in `task.json`, and stale helper files for non-string fields are removed.
|
package/dist/base-command.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Command } from "@oclif/core";
|
|
|
2
2
|
import { MechanicClient } from "./client.js";
|
|
3
3
|
import type { Project } from "./types.js";
|
|
4
4
|
export declare abstract class BaseCommand extends Command {
|
|
5
|
+
protected finally(error: Error | undefined): Promise<void>;
|
|
5
6
|
loadProject(): Promise<Project>;
|
|
6
7
|
clientForProject(project: Project): Promise<MechanicClient>;
|
|
7
8
|
verifiedClientForProject(project: Project): Promise<MechanicClient>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-command.d.ts","sourceRoot":"","sources":["../src/base-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAM,MAAM,aAAa,CAAC;AAI1C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"base-command.d.ts","sourceRoot":"","sources":["../src/base-command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAM,MAAM,aAAa,CAAC;AAI1C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAI7C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,8BAAsB,WAAY,SAAQ,OAAO;cACtB,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBnE,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAI/B,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,cAAc,CAAC;IAQ3D,wBAAwB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,cAAc,CAAC;IAMnE,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAY/E,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAI5B,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAenC,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM;IAI1C,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAI3B,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAIhC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAI7B,eAAe,IAAI,MAAM;IAQzB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAI9B,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;IAIhD,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,IAAI;CAgB9B"}
|
package/dist/base-command.js
CHANGED
|
@@ -5,13 +5,28 @@ import { requireToken } from "./auth.js";
|
|
|
5
5
|
import { MechanicClient } from "./client.js";
|
|
6
6
|
import { loadProject, taskAdminUrl } from "./config.js";
|
|
7
7
|
import { CliError } from "./errors.js";
|
|
8
|
+
import { getUpdateNotice, shouldCheckForUpdates } from "./update-check.js";
|
|
8
9
|
export class BaseCommand extends Command {
|
|
10
|
+
async finally(error) {
|
|
11
|
+
await super.finally(error);
|
|
12
|
+
if (error || !shouldCheckForUpdates(this.argv)) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const notice = await getUpdateNotice();
|
|
16
|
+
if (!notice) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
this.log("");
|
|
20
|
+
this.log(`${this.color("yellow", "Update available")} Mechanic CLI ${this.muted(notice.currentVersion)} -> ${this.success(notice.latestVersion)}`);
|
|
21
|
+
this.log(`Update with: ${this.taskName("npm install -g @lightward/mechanic-cli")}`);
|
|
22
|
+
}
|
|
9
23
|
async loadProject() {
|
|
10
24
|
return loadProject(process.cwd());
|
|
11
25
|
}
|
|
12
26
|
async clientForProject(project) {
|
|
13
27
|
return new MechanicClient({
|
|
14
28
|
baseUrl: project.apiBaseUrl,
|
|
29
|
+
expectedShopDomain: project.shopDomain,
|
|
15
30
|
token: await requireToken(project.shopDomain),
|
|
16
31
|
});
|
|
17
32
|
}
|
|
@@ -24,7 +39,7 @@ export class BaseCommand extends Command {
|
|
|
24
39
|
const verification = await client.verifyAuth();
|
|
25
40
|
const verifiedShopDomain = verification.shop?.shopify_domain;
|
|
26
41
|
if (verifiedShopDomain !== project.shopDomain) {
|
|
27
|
-
throw new CliError(
|
|
42
|
+
throw new CliError("API token does not match this Mechanic CLI project. Use a token for the shop in mechanic.json, or re-run mechanic init for the intended shop.", 2);
|
|
28
43
|
}
|
|
29
44
|
}
|
|
30
45
|
accent(text) {
|
package/dist/client.d.ts
CHANGED
|
@@ -17,9 +17,11 @@ export type AuthVerification = {
|
|
|
17
17
|
};
|
|
18
18
|
export declare class MechanicClient {
|
|
19
19
|
readonly baseUrl: string;
|
|
20
|
+
readonly expectedShopDomain?: string;
|
|
20
21
|
readonly token: string;
|
|
21
|
-
constructor({ baseUrl, token }: {
|
|
22
|
+
constructor({ baseUrl, expectedShopDomain, token }: {
|
|
22
23
|
baseUrl: string;
|
|
24
|
+
expectedShopDomain?: string;
|
|
23
25
|
token: string;
|
|
24
26
|
});
|
|
25
27
|
request<T>(pathname: string, options?: RequestOptions): Promise<T>;
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,YAAY,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAKtH,eAAO,MAAM,UAAU,QAAkD,CAAC;AAE1E,KAAK,cAAc,GAAG;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE;QACJ,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,SAAS,CAAC,EAAE;QACV,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACrB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC5B,CAAC;CACH,CAAC;AAwGF,qBAAa,cAAc;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBAEX,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,YAAY,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAKtH,eAAO,MAAM,UAAU,QAAkD,CAAC;AAE1E,KAAK,cAAc,GAAG;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE;QACJ,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,SAAS,CAAC,EAAE;QACV,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACrB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC5B,CAAC;CACH,CAAC;AAwGF,qBAAa,cAAc;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBAEX,EAAE,OAAO,EAAE,kBAAkB,EAAE,KAAK,EAAE,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;IAM7G,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,CAAC,CAAC;IAgD5E,UAAU,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAIvC,aAAa,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAI5C,SAAS,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAItC,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAI1C,WAAW,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IASxE,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAM3D,UAAU,CAAC,IAAI,EAAE,UAAU,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAQ5E,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE;QAAE,IAAI,EAAE,UAAU,CAAC;QAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,YAAY,CAAC;CAM3H"}
|
package/dist/client.js
CHANGED
|
@@ -87,9 +87,11 @@ function apiErrorMessage(response, parsed) {
|
|
|
87
87
|
}
|
|
88
88
|
export class MechanicClient {
|
|
89
89
|
baseUrl;
|
|
90
|
+
expectedShopDomain;
|
|
90
91
|
token;
|
|
91
|
-
constructor({ baseUrl, token }) {
|
|
92
|
+
constructor({ baseUrl, expectedShopDomain, token }) {
|
|
92
93
|
this.baseUrl = baseUrl;
|
|
94
|
+
this.expectedShopDomain = expectedShopDomain;
|
|
93
95
|
this.token = token;
|
|
94
96
|
}
|
|
95
97
|
async request(pathname, options = {}) {
|
|
@@ -106,6 +108,7 @@ export class MechanicClient {
|
|
|
106
108
|
Accept: "application/json",
|
|
107
109
|
Authorization: `Bearer ${this.token}`,
|
|
108
110
|
"User-Agent": USER_AGENT,
|
|
111
|
+
...(this.expectedShopDomain ? { "X-Mechanic-Shop-Domain": this.expectedShopDomain } : {}),
|
|
109
112
|
...(options.body === undefined ? {} : { "Content-Type": "application/json" }),
|
|
110
113
|
...(options.headers || {}),
|
|
111
114
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/login.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAIpD,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,WAAW;IAChD,OAAgB,WAAW,SAA4C;IAEvE,OAAgB,KAAK;;MAInB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/login.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAIpD,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,WAAW;IAChD,OAAgB,WAAW,SAA4C;IAEvE,OAAgB,KAAK;;MAInB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IA4BpB,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;CASxC"}
|
|
@@ -14,14 +14,18 @@ export default class AuthLogin extends BaseCommand {
|
|
|
14
14
|
const { flags } = await this.parse(AuthLogin);
|
|
15
15
|
const token = flags.token || process.env.MECHANIC_API_TOKEN || (await this.promptForToken());
|
|
16
16
|
const project = await this.loadProject();
|
|
17
|
-
const client = new MechanicClient({
|
|
17
|
+
const client = new MechanicClient({
|
|
18
|
+
baseUrl: project.apiBaseUrl,
|
|
19
|
+
expectedShopDomain: project.shopDomain,
|
|
20
|
+
token,
|
|
21
|
+
});
|
|
18
22
|
const verification = await client.verifyAuth();
|
|
19
23
|
const verifiedDomain = verification.shop?.shopify_domain;
|
|
20
24
|
if (!verifiedDomain) {
|
|
21
25
|
throw new CliError("Token verification did not return a shop domain.");
|
|
22
26
|
}
|
|
23
27
|
if (verifiedDomain !== project.shopDomain) {
|
|
24
|
-
throw new CliError(
|
|
28
|
+
throw new CliError("API token does not match this Mechanic CLI project. Use a token for the shop in mechanic.json.", 2);
|
|
25
29
|
}
|
|
26
30
|
await saveToken(project.shopDomain, token);
|
|
27
31
|
this.log(`${this.success("Stored")} API token for ${this.accent(project.shopDomain)}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAiBjD,MAAM,CAAC,OAAO,OAAO,MAAO,SAAQ,WAAW;IAC7C,OAAgB,WAAW,SAA2E;IAEhG,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAiBjD,MAAM,CAAC,OAAO,OAAO,MAAO,SAAQ,WAAW;IAC7C,OAAgB,WAAW,SAA2E;IAEhG,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IAoH1B,WAAW,CAAC,MAAM,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM;CAUpD"}
|
package/dist/commands/doctor.js
CHANGED
|
@@ -60,14 +60,16 @@ export default class Doctor extends BaseCommand {
|
|
|
60
60
|
const verifiedDomain = verification.shop?.shopify_domain;
|
|
61
61
|
verifiedShopMatches = verifiedDomain === project.shopDomain;
|
|
62
62
|
addRow("Token shop", verifiedShopMatches ? "ok" : "fail", verifiedDomain
|
|
63
|
-
?
|
|
63
|
+
? (verifiedShopMatches ? project.shopDomain : "API token does not match this project")
|
|
64
64
|
: "API did not return a shop domain");
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
65
|
+
if (verifiedShopMatches) {
|
|
66
|
+
const tokenDetails = [
|
|
67
|
+
verification.api_token?.name,
|
|
68
|
+
verification.api_token?.created_by ? `created by ${verification.api_token.created_by}` : null,
|
|
69
|
+
].filter(Boolean).join(", ");
|
|
70
|
+
if (tokenDetails) {
|
|
71
|
+
addRow("Token identity", "ok", tokenDetails);
|
|
72
|
+
}
|
|
71
73
|
}
|
|
72
74
|
}
|
|
73
75
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AASjD,MAAM,CAAC,OAAO,OAAO,IAAK,SAAQ,WAAW;IAC3C,OAAgB,WAAW,SAAkD;IAE7E,OAAgB,KAAK;;;;;;MAQnB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AASjD,MAAM,CAAC,OAAO,OAAO,IAAK,SAAQ,WAAW;IAC3C,OAAgB,WAAW,SAAkD;IAE7E,OAAgB,KAAK;;;;;;MAQnB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAoE3B"}
|
package/dist/commands/init.js
CHANGED
|
@@ -28,14 +28,18 @@ export default class Init extends BaseCommand {
|
|
|
28
28
|
: null);
|
|
29
29
|
let storedToken = false;
|
|
30
30
|
if (token) {
|
|
31
|
-
const client = new MechanicClient({
|
|
31
|
+
const client = new MechanicClient({
|
|
32
|
+
baseUrl: normalizeApiBaseUrl(flags["api-base-url"]),
|
|
33
|
+
expectedShopDomain: flags.shop,
|
|
34
|
+
token,
|
|
35
|
+
});
|
|
32
36
|
const verification = await client.verifyAuth();
|
|
33
37
|
const verifiedDomain = verification.shop?.shopify_domain;
|
|
34
38
|
if (!verifiedDomain) {
|
|
35
39
|
throw new CliError("Token verification did not return a shop domain.");
|
|
36
40
|
}
|
|
37
41
|
if (verifiedDomain !== flags.shop) {
|
|
38
|
-
throw new CliError(
|
|
42
|
+
throw new CliError("API token does not match the requested shop. Check --shop or use a token for that shop.", 2);
|
|
39
43
|
}
|
|
40
44
|
}
|
|
41
45
|
const project = await initProject(process.cwd(), flags.shop, flags["api-base-url"], flags["app-url"], Boolean(flags.force));
|
|
@@ -61,11 +65,13 @@ export default class Init extends BaseCommand {
|
|
|
61
65
|
this.log(` ${this.taskName("mechanic auth login")}`);
|
|
62
66
|
}
|
|
63
67
|
this.log(` ${this.taskName("mechanic tasks pull")}`);
|
|
68
|
+
this.log(" or, to start from scratch:");
|
|
69
|
+
this.log(` ${this.taskName("mechanic tasks new <task-slug>")}`);
|
|
64
70
|
this.log(` ${this.taskName("mechanic tasks status")}`);
|
|
65
|
-
this.log(` ${this.taskName("mechanic tasks preview
|
|
66
|
-
this.log(` ${this.taskName("mechanic tasks diff
|
|
67
|
-
this.log(` ${this.taskName("mechanic tasks publish
|
|
68
|
-
this.log(` ${this.taskName("mechanic tasks publish
|
|
71
|
+
this.log(` ${this.taskName("mechanic tasks preview <task-slug>")}`);
|
|
72
|
+
this.log(` ${this.taskName("mechanic tasks diff <task-slug>")}`);
|
|
73
|
+
this.log(` ${this.taskName("mechanic tasks publish <task-slug> --dry-run")}`);
|
|
74
|
+
this.log(` ${this.taskName("mechanic tasks publish <task-slug>")}`);
|
|
69
75
|
this.log("");
|
|
70
76
|
this.log(this.lightwardAiLine());
|
|
71
77
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["../../../src/commands/tasks/bundle.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"bundle.d.ts","sourceRoot":"","sources":["../../../src/commands/tasks/bundle.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AASpD,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,WAAW;IAClD,OAAgB,OAAO,SAA+D;IACtF,OAAgB,WAAW,SAAqG;IAChI,OAAgB,QAAQ,WAKtB;IAEF,OAAgB,IAAI;;MAElB;IAEF,OAAgB,KAAK;;;MAOnB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;YAgCZ,kBAAkB;CAuEjC"}
|
|
@@ -3,17 +3,21 @@ import path from "node:path";
|
|
|
3
3
|
import { BaseCommand } from "../../base-command.js";
|
|
4
4
|
import { CliError } from "../../errors.js";
|
|
5
5
|
import { pathExists } from "../../fs.js";
|
|
6
|
-
import { bundleTask } from "../../tasks.js";
|
|
6
|
+
import { bundleTask, displayTaskPath, resolveTaskSelector } from "../../tasks.js";
|
|
7
|
+
function looksLikeTaskPath(input) {
|
|
8
|
+
return input.includes("/") || input.includes("\\") || input.toLowerCase().endsWith(".json");
|
|
9
|
+
}
|
|
7
10
|
export default class TasksBundle extends BaseCommand {
|
|
8
11
|
static summary = "Bundle one helper task directory into one task JSON file.";
|
|
9
|
-
static description = "Bundle one editable helper task directory into one canonical task JSON file.";
|
|
12
|
+
static description = "Bundle one local task slug or editable helper task directory into one canonical task JSON file.";
|
|
10
13
|
static examples = [
|
|
14
|
+
"$ mechanic tasks bundle order-tagger",
|
|
11
15
|
"$ mechanic tasks bundle tasks/order-tagger",
|
|
12
16
|
"$ mechanic tasks bundle tasks/order-tagger.json",
|
|
13
|
-
"$ mechanic tasks bundle
|
|
17
|
+
"$ mechanic tasks bundle order-tagger --out tasks/order-tagger.json",
|
|
14
18
|
];
|
|
15
19
|
static args = {
|
|
16
|
-
dir: Args.string({ required: false, description: "
|
|
20
|
+
dir: Args.string({ required: false, description: "Local task slug, helper task directory, or matching task JSON file." }),
|
|
17
21
|
};
|
|
18
22
|
static flags = {
|
|
19
23
|
json: Flags.boolean({
|
|
@@ -30,7 +34,7 @@ export default class TasksBundle extends BaseCommand {
|
|
|
30
34
|
"Choose one helper task directory to bundle.",
|
|
31
35
|
"",
|
|
32
36
|
"Example:",
|
|
33
|
-
" mechanic tasks bundle
|
|
37
|
+
" mechanic tasks bundle order-tagger",
|
|
34
38
|
"",
|
|
35
39
|
"By default this writes to tasks/order-tagger.json. Use --out <file> to choose another task JSON file.",
|
|
36
40
|
].join("\n"), 2);
|
|
@@ -59,6 +63,34 @@ export default class TasksBundle extends BaseCommand {
|
|
|
59
63
|
outputDisplay: out || `${input}.json`,
|
|
60
64
|
};
|
|
61
65
|
}
|
|
66
|
+
if (!looksLikeTaskPath(input)) {
|
|
67
|
+
let project = null;
|
|
68
|
+
try {
|
|
69
|
+
project = await this.loadProject();
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
if (!(error instanceof CliError) || !error.message.startsWith("No mechanic.json found")) {
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (project) {
|
|
77
|
+
const selector = await resolveTaskSelector(project, input, { allowHelperDir: true });
|
|
78
|
+
const filePath = selector.file ? path.resolve(selector.file) : null;
|
|
79
|
+
const helperPath = selector.helperDir
|
|
80
|
+
? path.resolve(selector.helperDir)
|
|
81
|
+
: filePath?.replace(/\.json$/i, "");
|
|
82
|
+
if (helperPath && await pathExists(path.join(helperPath, "task.json"))) {
|
|
83
|
+
const outputPath = out
|
|
84
|
+
? path.resolve(out)
|
|
85
|
+
: filePath || `${helperPath}.json`;
|
|
86
|
+
return {
|
|
87
|
+
dirPath: helperPath,
|
|
88
|
+
inputDisplay: displayTaskPath(project, helperPath),
|
|
89
|
+
outputDisplay: displayTaskPath(project, outputPath),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
62
94
|
if (!input.toLowerCase().endsWith(".json")) {
|
|
63
95
|
return {
|
|
64
96
|
dirPath: inputPath,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../../src/commands/tasks/diff.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAYpD,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,WAAW;IAChD,OAAgB,OAAO,SAA2D;IAClF,OAAgB,WAAW,SAA6G;IAExI,OAAgB,IAAI;;MAElB;IAEF,OAAgB,KAAK;;;;MAMnB;IAEF,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IA2BzB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../../src/commands/tasks/diff.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAYpD,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,WAAW;IAChD,OAAgB,OAAO,SAA2D;IAClF,OAAgB,WAAW,SAA6G;IAExI,OAAgB,IAAI;;MAElB;IAEF,OAAgB,KAAK;;;;MAMnB;IAEF,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IA2BzB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAoH3B"}
|
|
@@ -6,7 +6,7 @@ export default class TasksDiff extends BaseCommand {
|
|
|
6
6
|
static summary = "Diff one task, or use --all to diff every local task.";
|
|
7
7
|
static description = "Compare one local task with its linked remote task, or use --all to compare every local task JSON file.";
|
|
8
8
|
static args = {
|
|
9
|
-
file: Args.string({ required: false, description: "
|
|
9
|
+
file: Args.string({ required: false, description: "Local task slug, task file, helper directory, or linked task ID." }),
|
|
10
10
|
};
|
|
11
11
|
static flags = {
|
|
12
12
|
all: Flags.boolean({ description: "Diff every task JSON file in this project." }),
|
|
@@ -86,7 +86,7 @@ export default class TasksDiff extends BaseCommand {
|
|
|
86
86
|
if (remoteChanged || diff) {
|
|
87
87
|
const section = [`# ${relativeFile}`];
|
|
88
88
|
if (remoteChanged) {
|
|
89
|
-
section.push(this.color("yellow", "Remote changed since your last pull."), this.muted("Showing the current Mechanic task
|
|
89
|
+
section.push(this.color("yellow", "Remote changed since your last pull."), this.muted("Showing what would change if your local file replaced the current Mechanic task."), this.muted("Diff legend: - current Mechanic, + local file."), this.muted(`Run "mechanic tasks pull ${link.remote_id}" to keep the Mechanic version, or use --force only if the local file should win.`), "");
|
|
90
90
|
}
|
|
91
91
|
if (diff) {
|
|
92
92
|
section.push(diff);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { BaseCommand } from "../../base-command.js";
|
|
2
|
+
export default class TasksNew extends BaseCommand {
|
|
3
|
+
static summary: string;
|
|
4
|
+
static description: string;
|
|
5
|
+
static examples: string[];
|
|
6
|
+
static args: {
|
|
7
|
+
name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
8
|
+
};
|
|
9
|
+
static flags: {
|
|
10
|
+
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
12
|
+
};
|
|
13
|
+
run(): Promise<void>;
|
|
14
|
+
private checkExistingPaths;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=new.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"new.d.ts","sourceRoot":"","sources":["../../../src/commands/tasks/new.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAwCpD,MAAM,CAAC,OAAO,OAAO,QAAS,SAAQ,WAAW;IAC/C,OAAgB,OAAO,SAAoC;IAC3D,OAAgB,WAAW,SAAiJ;IAC5K,OAAgB,QAAQ,WAGtB;IAEF,OAAgB,IAAI;;MAElB;IAEF,OAAgB,KAAK;;;MAGnB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;YA0CZ,kBAAkB;CA2BjC"}
|