@lightward/mechanic-cli 0.1.8 → 0.1.10
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 +34 -7
- package/dist/client.d.ts +2 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +4 -0
- package/dist/commands/github/init.d.ts.map +1 -1
- package/dist/commands/github/init.js +2 -2
- package/dist/commands/globals/delete.d.ts +13 -0
- package/dist/commands/globals/delete.d.ts.map +1 -0
- package/dist/commands/globals/delete.js +28 -0
- package/dist/commands/globals/list.d.ts +10 -0
- package/dist/commands/globals/list.d.ts.map +1 -0
- package/dist/commands/globals/list.js +39 -0
- package/dist/commands/globals/pull.d.ts +11 -0
- package/dist/commands/globals/pull.d.ts.map +1 -0
- package/dist/commands/globals/pull.js +42 -0
- package/dist/commands/globals/push.d.ts +11 -0
- package/dist/commands/globals/push.d.ts.map +1 -0
- package/dist/commands/globals/push.js +57 -0
- package/dist/commands/globals/set.d.ts +13 -0
- package/dist/commands/globals/set.d.ts.map +1 -0
- package/dist/commands/globals/set.js +26 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +1 -0
- package/dist/commands/secrets/delete.d.ts +13 -0
- package/dist/commands/secrets/delete.d.ts.map +1 -0
- package/dist/commands/secrets/delete.js +28 -0
- package/dist/commands/secrets/list.d.ts +10 -0
- package/dist/commands/secrets/list.d.ts.map +1 -0
- package/dist/commands/secrets/list.js +37 -0
- package/dist/commands/secrets/set.d.ts +25 -0
- package/dist/commands/secrets/set.d.ts.map +1 -0
- package/dist/commands/secrets/set.js +91 -0
- package/dist/commands/shop/deprecations.d.ts +20 -0
- package/dist/commands/shop/deprecations.d.ts.map +1 -0
- package/dist/commands/shop/deprecations.js +95 -0
- package/dist/commands/tasks/diff.d.ts.map +1 -1
- package/dist/commands/tasks/diff.js +4 -3
- package/dist/commands/tasks/list.d.ts.map +1 -1
- package/dist/commands/tasks/list.js +12 -1
- package/dist/commands/tasks/new.d.ts.map +1 -1
- package/dist/commands/tasks/new.js +10 -2
- package/dist/commands/tasks/push.d.ts +2 -1
- package/dist/commands/tasks/push.d.ts.map +1 -1
- package/dist/commands/tasks/push.js +43 -20
- package/dist/commands/tasks/status.d.ts +22 -4
- package/dist/commands/tasks/status.d.ts.map +1 -1
- package/dist/commands/tasks/status.js +66 -9
- package/dist/config-keys.d.ts +2 -0
- package/dist/config-keys.d.ts.map +1 -0
- package/dist/config-keys.js +8 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +34 -3
- package/dist/types.d.ts +24 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +10 -3
package/README.md
CHANGED
|
@@ -56,7 +56,17 @@ prompted:
|
|
|
56
56
|
mechanic init --shop example.myshopify.com
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
Choose how to start.
|
|
60
|
+
|
|
61
|
+
To bring one existing task into local files, find its remote task ID, then pull
|
|
62
|
+
that task:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
mechanic tasks list --verbose
|
|
66
|
+
mechanic tasks pull <remote-task-id>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
To intentionally bootstrap a repo with every task in the shop:
|
|
60
70
|
|
|
61
71
|
```bash
|
|
62
72
|
mechanic tasks pull
|
|
@@ -70,15 +80,14 @@ initialized CLI project; it does not require a fresh repository:
|
|
|
70
80
|
mechanic tasks new order-tagger
|
|
71
81
|
```
|
|
72
82
|
|
|
73
|
-
For most Liquid or documentation edits,
|
|
74
|
-
|
|
75
|
-
|
|
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.
|
|
83
|
+
For most Liquid or documentation edits, use the helper directory beside the JSON
|
|
84
|
+
file. Existing pulled tasks can be unbundled first; tasks created with
|
|
85
|
+
`tasks new` already have the helper directory.
|
|
79
86
|
|
|
80
87
|
```bash
|
|
88
|
+
# for an existing pulled task only
|
|
81
89
|
mechanic tasks unbundle order-tagger
|
|
90
|
+
|
|
82
91
|
# edit tasks/order-tagger/script.liquid, docs.md, or task.json
|
|
83
92
|
mechanic tasks bundle order-tagger
|
|
84
93
|
mechanic tasks status
|
|
@@ -88,6 +97,10 @@ mechanic tasks publish order-tagger --dry-run
|
|
|
88
97
|
mechanic tasks publish order-tagger
|
|
89
98
|
```
|
|
90
99
|
|
|
100
|
+
For example, edit `tasks/order-tagger/docs.md` or
|
|
101
|
+
`tasks/order-tagger/script.liquid`, bundle it, preview it, then publish only
|
|
102
|
+
when the diff and dry-run plan look right.
|
|
103
|
+
|
|
91
104
|
For normal setup, paste the token into the masked prompt or run
|
|
92
105
|
`mechanic auth login` after `mechanic init`. In CI, store the token as
|
|
93
106
|
`MECHANIC_API_TOKEN`. The `--token` flag exists for controlled automation, but
|
|
@@ -113,6 +126,7 @@ mechanic auth login [--token <token>]
|
|
|
113
126
|
mechanic auth logout
|
|
114
127
|
mechanic github init [--force]
|
|
115
128
|
mechanic shop status [--json]
|
|
129
|
+
mechanic shop deprecations [task] [--json]
|
|
116
130
|
mechanic tasks list [--verbose] [--json]
|
|
117
131
|
mechanic tasks new <name> [--force] [--json]
|
|
118
132
|
mechanic tasks open <task>
|
|
@@ -178,6 +192,19 @@ unless you pass `--force`. It does not create anything in Mechanic until you run
|
|
|
178
192
|
running runs, waiting runs, queue lag, and the largest backlog groups by task,
|
|
179
193
|
action, and event topic.
|
|
180
194
|
|
|
195
|
+
`shop deprecations` shows unresolved Shopify API deprecations reported by tasks
|
|
196
|
+
in the configured shop. Pass a task slug, task file, helper directory, or linked
|
|
197
|
+
remote task ID to focus on one task:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
mechanic shop deprecations
|
|
201
|
+
mechanic shop deprecations order-tagger
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
The output includes the task's configured Shopify API version and the Shopify API
|
|
205
|
+
version seen in the deprecated request. Use `--json` for monitoring scripts,
|
|
206
|
+
agents, or dashboards.
|
|
207
|
+
|
|
181
208
|
`tasks status` shows whether local task JSON files are linked, ready to publish,
|
|
182
209
|
and in sync with their remote Mechanic tasks. Use `--local` when you only want
|
|
183
210
|
the fast offline check for JSON validity, link state, and helper-folder drift.
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { JsonObject, ShopStatusResponse, TaskEnvelope, TaskListResponse, TaskPreviewResponse } from "./types.js";
|
|
1
|
+
import type { JsonObject, ShopDeprecationsResponse, ShopStatusResponse, TaskEnvelope, TaskListResponse, TaskPreviewResponse } from "./types.js";
|
|
2
2
|
export declare const USER_AGENT: string;
|
|
3
3
|
type RequestOptions = {
|
|
4
4
|
method?: string;
|
|
@@ -27,6 +27,7 @@ export declare class MechanicClient {
|
|
|
27
27
|
request<T>(pathname: string, options?: RequestOptions): Promise<T>;
|
|
28
28
|
verifyAuth(): Promise<AuthVerification>;
|
|
29
29
|
getShopStatus(): Promise<ShopStatusResponse>;
|
|
30
|
+
getShopDeprecations(taskId?: string): Promise<ShopDeprecationsResponse>;
|
|
30
31
|
listTasks(): Promise<TaskListResponse>;
|
|
31
32
|
getTask(id: string): Promise<TaskEnvelope>;
|
|
32
33
|
previewTask(task: JsonObject, id?: string): Promise<TaskPreviewResponse>;
|
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,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,UAAU,EACV,wBAAwB,EACxB,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EAChB,mBAAmB,EACpB,MAAM,YAAY,CAAC;AAKpB,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,CAAC,EAAE;QACL,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAChC,GAAG,IAAI,CAAC;IACT,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,mBAAmB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAMvE,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
|
@@ -141,6 +141,10 @@ export class MechanicClient {
|
|
|
141
141
|
getShopStatus() {
|
|
142
142
|
return this.request("/v1/shop/status");
|
|
143
143
|
}
|
|
144
|
+
getShopDeprecations(taskId) {
|
|
145
|
+
const search = taskId ? `?${new URLSearchParams({ task_id: taskId }).toString()}` : "";
|
|
146
|
+
return this.request(`/v1/shop/deprecations${search}`);
|
|
147
|
+
}
|
|
144
148
|
listTasks() {
|
|
145
149
|
return this.request("/v1/tasks");
|
|
146
150
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/commands/github/init.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAKpD,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,WAAW;IACjD,OAAgB,OAAO,
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/commands/github/init.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAKpD,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,WAAW;IACjD,OAAgB,OAAO,SAAqF;IAC5G,OAAgB,WAAW,SAA0H;IAErJ,OAAgB,KAAK;;MAEnB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CA2C3B"}
|
|
@@ -5,8 +5,8 @@ import { CliError } from "../../errors.js";
|
|
|
5
5
|
import { ensureDir, pathExists, writeText } from "../../fs.js";
|
|
6
6
|
import { githubWorkflowFiles } from "../../github-workflows.js";
|
|
7
7
|
export default class GithubInit extends BaseCommand {
|
|
8
|
-
static summary = "Create GitHub Actions workflows for single-shop Mechanic task updates.";
|
|
9
|
-
static description = "Create validation, manual deploy, and pull-from-app GitHub Actions workflows for this Mechanic CLI project.";
|
|
8
|
+
static summary = "Create optional GitHub Actions workflows for single-shop Mechanic task updates.";
|
|
9
|
+
static description = "Create optional validation, manual deploy, and pull-from-app GitHub Actions workflows for this Mechanic CLI project.";
|
|
10
10
|
static flags = {
|
|
11
11
|
force: Flags.boolean({ description: "Overwrite existing Mechanic GitHub workflow files." }),
|
|
12
12
|
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BaseCommand } from "../../base-command.js";
|
|
2
|
+
export default class GlobalsDelete extends BaseCommand {
|
|
3
|
+
static summary: string;
|
|
4
|
+
static description: string;
|
|
5
|
+
static args: {
|
|
6
|
+
key: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
static flags: {
|
|
9
|
+
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
};
|
|
11
|
+
run(): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=delete.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["../../../src/commands/globals/delete.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAIpD,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,WAAW;IACpD,OAAgB,OAAO,SAA6B;IACpD,OAAgB,WAAW,SAA2C;IAEtE,OAAgB,IAAI;;MAElB;IAEF,OAAgB,KAAK;;MAKnB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAc3B"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Args, Flags } from "@oclif/core";
|
|
2
|
+
import { BaseCommand } from "../../base-command.js";
|
|
3
|
+
import { validateConfigKey } from "../../config-keys.js";
|
|
4
|
+
import { CliError } from "../../errors.js";
|
|
5
|
+
export default class GlobalsDelete extends BaseCommand {
|
|
6
|
+
static summary = "Delete one shop global.";
|
|
7
|
+
static description = "Delete one visible shop-level global.";
|
|
8
|
+
static args = {
|
|
9
|
+
key: Args.string({ required: true, description: "Global key, using lower snake-case." }),
|
|
10
|
+
};
|
|
11
|
+
static flags = {
|
|
12
|
+
force: Flags.boolean({
|
|
13
|
+
char: "f",
|
|
14
|
+
description: "Delete without an interactive confirmation.",
|
|
15
|
+
}),
|
|
16
|
+
};
|
|
17
|
+
async run() {
|
|
18
|
+
const { args, flags } = await this.parse(GlobalsDelete);
|
|
19
|
+
const key = validateConfigKey(args.key);
|
|
20
|
+
if (!flags.force) {
|
|
21
|
+
throw new CliError(`Deleting global ${key} cannot be undone. Re-run with --force to confirm.`, 2);
|
|
22
|
+
}
|
|
23
|
+
const project = await this.loadProject();
|
|
24
|
+
const client = await this.verifiedClientForProject(project);
|
|
25
|
+
await client.deleteGlobal(key);
|
|
26
|
+
this.log(`${this.success("Deleted")} global ${this.taskName(key)}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { BaseCommand } from "../../base-command.js";
|
|
2
|
+
export default class GlobalsList extends BaseCommand {
|
|
3
|
+
static summary: string;
|
|
4
|
+
static description: string;
|
|
5
|
+
static flags: {
|
|
6
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
};
|
|
8
|
+
run(): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/commands/globals/list.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAUpD,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,WAAW;IAClD,OAAgB,OAAO,SAAwB;IAC/C,OAAgB,WAAW,SAAqE;IAEhG,OAAgB,KAAK;;MAInB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAwB3B"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Flags } from "@oclif/core";
|
|
2
|
+
import { BaseCommand } from "../../base-command.js";
|
|
3
|
+
function formatValue(value) {
|
|
4
|
+
if (value === undefined) {
|
|
5
|
+
return "";
|
|
6
|
+
}
|
|
7
|
+
return JSON.stringify(value);
|
|
8
|
+
}
|
|
9
|
+
export default class GlobalsList extends BaseCommand {
|
|
10
|
+
static summary = "List shop globals.";
|
|
11
|
+
static description = "List visible shop-level JSON globals for this Mechanic project.";
|
|
12
|
+
static flags = {
|
|
13
|
+
json: Flags.boolean({
|
|
14
|
+
description: "Print globals as JSON for agents, scripts, or editor integrations.",
|
|
15
|
+
}),
|
|
16
|
+
};
|
|
17
|
+
async run() {
|
|
18
|
+
const { flags } = await this.parse(GlobalsList);
|
|
19
|
+
const project = await this.loadProject();
|
|
20
|
+
const client = await this.verifiedClientForProject(project);
|
|
21
|
+
const response = await client.listGlobals();
|
|
22
|
+
const globals = response.globals || [];
|
|
23
|
+
if (flags.json) {
|
|
24
|
+
this.outputJson({
|
|
25
|
+
shop_domain: project.shopDomain,
|
|
26
|
+
globals,
|
|
27
|
+
});
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
this.table([
|
|
31
|
+
["Key", "Value", "Updated"],
|
|
32
|
+
...globals.map((global) => [
|
|
33
|
+
this.taskName(global.key),
|
|
34
|
+
formatValue(global.value),
|
|
35
|
+
this.muted(global.updated_at || ""),
|
|
36
|
+
]),
|
|
37
|
+
]);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { BaseCommand } from "../../base-command.js";
|
|
2
|
+
export default class GlobalsPull extends BaseCommand {
|
|
3
|
+
static summary: string;
|
|
4
|
+
static description: string;
|
|
5
|
+
static flags: {
|
|
6
|
+
out: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
};
|
|
9
|
+
run(): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=pull.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../../src/commands/globals/pull.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAOpD,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,WAAW;IAClD,OAAgB,OAAO,SAAmD;IAC1E,OAAgB,WAAW,SAAiE;IAE5F,OAAgB,KAAK;;;MAQnB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CA0B3B"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Flags } from "@oclif/core";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { BaseCommand } from "../../base-command.js";
|
|
4
|
+
import { CliError } from "../../errors.js";
|
|
5
|
+
import { pathExists, readText, writeText } from "../../fs.js";
|
|
6
|
+
import { stableStringify } from "../../json.js";
|
|
7
|
+
const DEFAULT_GLOBALS_FILE = "mechanic.globals.json";
|
|
8
|
+
export default class GlobalsPull extends BaseCommand {
|
|
9
|
+
static summary = "Pull shop globals into mechanic.globals.json.";
|
|
10
|
+
static description = "Pull visible shop-level globals into a repo-safe JSON file.";
|
|
11
|
+
static flags = {
|
|
12
|
+
out: Flags.string({
|
|
13
|
+
description: "File path to write.",
|
|
14
|
+
default: DEFAULT_GLOBALS_FILE,
|
|
15
|
+
}),
|
|
16
|
+
force: Flags.boolean({
|
|
17
|
+
description: "Overwrite an existing globals file when its contents differ.",
|
|
18
|
+
}),
|
|
19
|
+
};
|
|
20
|
+
async run() {
|
|
21
|
+
const { flags } = await this.parse(GlobalsPull);
|
|
22
|
+
const project = await this.loadProject();
|
|
23
|
+
const client = await this.verifiedClientForProject(project);
|
|
24
|
+
const response = await client.listGlobals();
|
|
25
|
+
const globals = Object.fromEntries((response.globals || []).map((global) => [global.key, global.value]));
|
|
26
|
+
const file = path.resolve(project.cwd, flags.out);
|
|
27
|
+
const relativeFile = path.relative(project.cwd, file) || file;
|
|
28
|
+
const contents = `${stableStringify(globals)}\n`;
|
|
29
|
+
if (await pathExists(file)) {
|
|
30
|
+
const existingContents = await readText(file);
|
|
31
|
+
if (existingContents === contents) {
|
|
32
|
+
this.log(`${this.success("Unchanged")} ${this.taskName(relativeFile)}`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (!flags.force) {
|
|
36
|
+
throw new CliError(`${relativeFile} already exists and differs. Re-run with --force to overwrite.`, 2);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
await writeText(file, contents);
|
|
40
|
+
this.log(`${this.success("Wrote")} ${this.taskName(relativeFile)}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { BaseCommand } from "../../base-command.js";
|
|
2
|
+
export default class GlobalsPush extends BaseCommand {
|
|
3
|
+
static summary: string;
|
|
4
|
+
static description: string;
|
|
5
|
+
static flags: {
|
|
6
|
+
file: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
"dry-run": import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
};
|
|
9
|
+
run(): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=push.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../../src/commands/globals/push.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAgBpD,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,WAAW;IAClD,OAAgB,OAAO,SAA8C;IACrE,OAAgB,WAAW,SAAmI;IAE9J,OAAgB,KAAK;;;MAQnB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAuC3B"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Flags } from "@oclif/core";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { BaseCommand } from "../../base-command.js";
|
|
4
|
+
import { validateConfigKey } from "../../config-keys.js";
|
|
5
|
+
import { CliError } from "../../errors.js";
|
|
6
|
+
import { readJson } from "../../fs.js";
|
|
7
|
+
import { stableStringify } from "../../json.js";
|
|
8
|
+
const DEFAULT_GLOBALS_FILE = "mechanic.globals.json";
|
|
9
|
+
function ensurePlainObject(value, source) {
|
|
10
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || Object.getPrototypeOf(value) !== Object.prototype) {
|
|
11
|
+
throw new CliError(`${source} must contain a JSON object mapping global keys to values.`, 2);
|
|
12
|
+
}
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
export default class GlobalsPush extends BaseCommand {
|
|
16
|
+
static summary = "Push mechanic.globals.json to this shop.";
|
|
17
|
+
static description = "Push visible shop-level globals from a repo-safe JSON file. This updates listed keys and does not delete missing remote keys.";
|
|
18
|
+
static flags = {
|
|
19
|
+
file: Flags.string({
|
|
20
|
+
description: "Globals JSON file to read.",
|
|
21
|
+
default: DEFAULT_GLOBALS_FILE,
|
|
22
|
+
}),
|
|
23
|
+
"dry-run": Flags.boolean({
|
|
24
|
+
description: "Show what would be pushed without writing to Mechanic.",
|
|
25
|
+
}),
|
|
26
|
+
};
|
|
27
|
+
async run() {
|
|
28
|
+
const { flags } = await this.parse(GlobalsPush);
|
|
29
|
+
const project = await this.loadProject();
|
|
30
|
+
const client = await this.verifiedClientForProject(project);
|
|
31
|
+
const file = path.resolve(project.cwd, flags.file);
|
|
32
|
+
const entries = Object.entries(ensurePlainObject(await readJson(file), path.relative(project.cwd, file) || file));
|
|
33
|
+
const rows = [["Key", "Action"]];
|
|
34
|
+
for (const [key] of entries) {
|
|
35
|
+
validateConfigKey(key);
|
|
36
|
+
}
|
|
37
|
+
if (flags["dry-run"]) {
|
|
38
|
+
const remoteGlobals = Object.fromEntries((await client.listGlobals()).globals?.map((global) => [global.key, global.value]) || []);
|
|
39
|
+
for (const [key, value] of entries) {
|
|
40
|
+
const remoteValue = remoteGlobals[key];
|
|
41
|
+
const action = remoteValue === undefined
|
|
42
|
+
? "would create"
|
|
43
|
+
: stableStringify(remoteValue) === stableStringify(value)
|
|
44
|
+
? "unchanged"
|
|
45
|
+
: "would update";
|
|
46
|
+
rows.push([this.taskName(key), this.actionLabel(action)]);
|
|
47
|
+
}
|
|
48
|
+
this.table(rows);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
for (const [key, value] of entries) {
|
|
52
|
+
await client.setGlobal(key, value);
|
|
53
|
+
rows.push([this.taskName(key), this.actionLabel("updated")]);
|
|
54
|
+
}
|
|
55
|
+
this.table(rows);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BaseCommand } from "../../base-command.js";
|
|
2
|
+
export default class GlobalsSet extends BaseCommand {
|
|
3
|
+
static summary: string;
|
|
4
|
+
static description: string;
|
|
5
|
+
static args: {
|
|
6
|
+
key: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
static flags: {
|
|
9
|
+
json: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
};
|
|
11
|
+
run(): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=set.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"set.d.ts","sourceRoot":"","sources":["../../../src/commands/globals/set.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAIpD,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,WAAW;IACjD,OAAgB,OAAO,SAA0B;IACjD,OAAgB,WAAW,SAA6C;IAExE,OAAgB,IAAI;;MAElB;IAEF,OAAgB,KAAK;;MAKnB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAU3B"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Args, Flags } from "@oclif/core";
|
|
2
|
+
import { BaseCommand } from "../../base-command.js";
|
|
3
|
+
import { validateConfigKey } from "../../config-keys.js";
|
|
4
|
+
import { parseJson } from "../../json.js";
|
|
5
|
+
export default class GlobalsSet extends BaseCommand {
|
|
6
|
+
static summary = "Set one shop global.";
|
|
7
|
+
static description = "Set one visible shop-level JSON global.";
|
|
8
|
+
static args = {
|
|
9
|
+
key: Args.string({ required: true, description: "Global key, using lower snake-case." }),
|
|
10
|
+
};
|
|
11
|
+
static flags = {
|
|
12
|
+
json: Flags.string({
|
|
13
|
+
required: true,
|
|
14
|
+
description: "JSON value to store.",
|
|
15
|
+
}),
|
|
16
|
+
};
|
|
17
|
+
async run() {
|
|
18
|
+
const { args, flags } = await this.parse(GlobalsSet);
|
|
19
|
+
const key = validateConfigKey(args.key);
|
|
20
|
+
const value = parseJson(flags.json, "--json");
|
|
21
|
+
const project = await this.loadProject();
|
|
22
|
+
const client = await this.verifiedClientForProject(project);
|
|
23
|
+
await client.setGlobal(key, value);
|
|
24
|
+
this.log(`${this.success("Set")} global ${this.taskName(key)}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -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;CAiE3B"}
|
package/dist/commands/init.js
CHANGED
|
@@ -59,6 +59,7 @@ export default class Init extends BaseCommand {
|
|
|
59
59
|
this.log("");
|
|
60
60
|
this.log("Next steps:");
|
|
61
61
|
if (!storedToken) {
|
|
62
|
+
this.log(" create an API token in Mechanic: Settings -> API tokens");
|
|
62
63
|
this.log(` ${this.taskName("mechanic auth login")}`);
|
|
63
64
|
}
|
|
64
65
|
this.log(` ${this.taskName("mechanic tasks pull")}`);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BaseCommand } from "../../base-command.js";
|
|
2
|
+
export default class SecretsDelete extends BaseCommand {
|
|
3
|
+
static summary: string;
|
|
4
|
+
static description: string;
|
|
5
|
+
static args: {
|
|
6
|
+
key: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
static flags: {
|
|
9
|
+
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
};
|
|
11
|
+
run(): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=delete.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["../../../src/commands/secrets/delete.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAIpD,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,WAAW;IACpD,OAAgB,OAAO,SAA6B;IACpD,OAAgB,WAAW,SAAqF;IAEhH,OAAgB,IAAI;;MAElB;IAEF,OAAgB,KAAK;;MAKnB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAc3B"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Args, Flags } from "@oclif/core";
|
|
2
|
+
import { BaseCommand } from "../../base-command.js";
|
|
3
|
+
import { validateConfigKey } from "../../config-keys.js";
|
|
4
|
+
import { CliError } from "../../errors.js";
|
|
5
|
+
export default class SecretsDelete extends BaseCommand {
|
|
6
|
+
static summary = "Delete one shop secret.";
|
|
7
|
+
static description = "Delete one write-only shop-level secret. Affected tasks may fail until updated.";
|
|
8
|
+
static args = {
|
|
9
|
+
key: Args.string({ required: true, description: "Secret key, using lower snake-case." }),
|
|
10
|
+
};
|
|
11
|
+
static flags = {
|
|
12
|
+
force: Flags.boolean({
|
|
13
|
+
char: "f",
|
|
14
|
+
description: "Delete without an interactive confirmation.",
|
|
15
|
+
}),
|
|
16
|
+
};
|
|
17
|
+
async run() {
|
|
18
|
+
const { args, flags } = await this.parse(SecretsDelete);
|
|
19
|
+
const key = validateConfigKey(args.key);
|
|
20
|
+
if (!flags.force) {
|
|
21
|
+
throw new CliError(`Deleting secret ${key} can break tasks until updated. Re-run with --force to confirm.`, 2);
|
|
22
|
+
}
|
|
23
|
+
const project = await this.loadProject();
|
|
24
|
+
const client = await this.verifiedClientForProject(project);
|
|
25
|
+
await client.deleteSecret(key);
|
|
26
|
+
this.log(`${this.success("Deleted")} secret ${this.taskName(key)}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { BaseCommand } from "../../base-command.js";
|
|
2
|
+
export default class SecretsList extends BaseCommand {
|
|
3
|
+
static summary: string;
|
|
4
|
+
static description: string;
|
|
5
|
+
static flags: {
|
|
6
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
};
|
|
8
|
+
run(): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/commands/secrets/list.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAMpD,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,WAAW;IAClD,OAAgB,OAAO,SAAwB;IAC/C,OAAgB,WAAW,SAAmF;IAE9G,OAAgB,KAAK;;MAInB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CAyB3B"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Flags } from "@oclif/core";
|
|
2
|
+
import { BaseCommand } from "../../base-command.js";
|
|
3
|
+
function formatCount(value) {
|
|
4
|
+
return typeof value === "number" ? value.toLocaleString() : "0";
|
|
5
|
+
}
|
|
6
|
+
export default class SecretsList extends BaseCommand {
|
|
7
|
+
static summary = "List shop secrets.";
|
|
8
|
+
static description = "List write-only shop-level secret metadata. Secret values are never returned.";
|
|
9
|
+
static flags = {
|
|
10
|
+
json: Flags.boolean({
|
|
11
|
+
description: "Print secret metadata as JSON for agents, scripts, or editor integrations.",
|
|
12
|
+
}),
|
|
13
|
+
};
|
|
14
|
+
async run() {
|
|
15
|
+
const { flags } = await this.parse(SecretsList);
|
|
16
|
+
const project = await this.loadProject();
|
|
17
|
+
const client = await this.verifiedClientForProject(project);
|
|
18
|
+
const response = await client.listSecrets();
|
|
19
|
+
const secrets = response.secrets || [];
|
|
20
|
+
if (flags.json) {
|
|
21
|
+
this.outputJson({
|
|
22
|
+
shop_domain: project.shopDomain,
|
|
23
|
+
secrets,
|
|
24
|
+
});
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
this.table([
|
|
28
|
+
["Key", "Value", "Task refs", "Updated"],
|
|
29
|
+
...secrets.map((secret) => [
|
|
30
|
+
this.taskName(secret.key),
|
|
31
|
+
secret.value_present ? this.success("set") : this.muted("missing"),
|
|
32
|
+
formatCount(secret.task_reference_count),
|
|
33
|
+
this.muted(secret.updated_at || ""),
|
|
34
|
+
]),
|
|
35
|
+
]);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { stdin } from "node:process";
|
|
2
|
+
import { BaseCommand } from "../../base-command.js";
|
|
3
|
+
type SecretStdin = typeof stdin;
|
|
4
|
+
export declare function readStdin(input?: SecretStdin): Promise<string>;
|
|
5
|
+
export declare function readSecretValueFromStdin(input?: SecretStdin): Promise<string>;
|
|
6
|
+
export declare function readSecretValueFromEnv(envName: string, env?: NodeJS.ProcessEnv): string;
|
|
7
|
+
export default class SecretsSet extends BaseCommand {
|
|
8
|
+
static summary: string;
|
|
9
|
+
static description: string;
|
|
10
|
+
static args: {
|
|
11
|
+
key: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
12
|
+
};
|
|
13
|
+
static flags: {
|
|
14
|
+
"from-stdin": import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
|
+
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
|
+
"value-env": import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
|
+
};
|
|
18
|
+
run(): Promise<void>;
|
|
19
|
+
secretValue(flags: {
|
|
20
|
+
"from-stdin"?: boolean;
|
|
21
|
+
"value-env"?: string;
|
|
22
|
+
}): Promise<string>;
|
|
23
|
+
}
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=set.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"set.d.ts","sourceRoot":"","sources":["../../../src/commands/secrets/set.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAErC,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAIpD,KAAK,WAAW,GAAG,OAAO,KAAK,CAAC;AAEhC,wBAAgB,SAAS,CAAC,KAAK,GAAE,WAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAmBrE;AAED,wBAAsB,wBAAwB,CAC5C,KAAK,GAAE,WAAmB,GACzB,OAAO,CAAC,MAAM,CAAC,CAQjB;AAED,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,EACf,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,CAYR;AAED,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,WAAW;IACjD,OAAgB,OAAO,SAA0B;IACjD,OAAgB,WAAW,SAAwE;IAEnG,OAAgB,IAAI;;MAElB;IAEF,OAAgB,KAAK;;;;MAanB;IAEI,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IAiBpB,WAAW,CAAC,KAAK,EAAE;QAAE,YAAY,CAAC,EAAE,OAAO,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC;CAsB5F"}
|