@catalyst-cloud/cli 0.8.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.
Files changed (157) hide show
  1. package/CHANGELOG.md +75 -0
  2. package/LICENSE +21 -0
  3. package/README.md +205 -0
  4. package/bin/catalyst-skills.js +8 -0
  5. package/bin/catalyst.js +5 -0
  6. package/bin/launch.js +154 -0
  7. package/dist/args.js +280 -0
  8. package/dist/ask.js +161 -0
  9. package/dist/browser.js +20 -0
  10. package/dist/cli.js +397 -0
  11. package/dist/config.js +241 -0
  12. package/dist/contract-types.js +4 -0
  13. package/dist/contract.js +184 -0
  14. package/dist/detach.js +10 -0
  15. package/dist/environment.js +207 -0
  16. package/dist/errors.js +27 -0
  17. package/dist/events.js +106 -0
  18. package/dist/execution.js +451 -0
  19. package/dist/oauth.js +300 -0
  20. package/dist/pagination.js +76 -0
  21. package/dist/prompt.js +35 -0
  22. package/dist/published.js +79 -0
  23. package/dist/query.js +248 -0
  24. package/dist/ready.js +380 -0
  25. package/dist/release.js +142 -0
  26. package/dist/replica.js +614 -0
  27. package/dist/runtime-store.js +135 -0
  28. package/dist/runtime-verb.js +66 -0
  29. package/dist/runtime.js +87 -0
  30. package/dist/sdk.js +29 -0
  31. package/dist/secret.js +190 -0
  32. package/dist/semver.js +18 -0
  33. package/dist/skill-shape.js +189 -0
  34. package/dist/skills.js +129 -0
  35. package/dist/transport.js +205 -0
  36. package/dist/ts-deps-loader.js +113 -0
  37. package/dist/watch/consumer.js +141 -0
  38. package/dist/watch/cursor-file.js +62 -0
  39. package/dist/watch.js +175 -0
  40. package/dist/write.js +224 -0
  41. package/package.json +60 -0
  42. package/skills/catalyst-github/SKILL.md +35 -0
  43. package/skills/catalyst-github/agents/openai.yaml +6 -0
  44. package/skills/catalyst-github/agents/portability.yaml +4 -0
  45. package/skills/catalyst-github/references/is-it-mergeable.md +57 -0
  46. package/skills/catalyst-github/references/what-a-pr-accumulates.md +61 -0
  47. package/skills/catalyst-github/scripts/is-it-mergeable.mjs +124 -0
  48. package/skills/catalyst-github/scripts/lib/cli.mjs +103 -0
  49. package/skills/catalyst-github/scripts/lib/credential.mjs +29 -0
  50. package/skills/catalyst-github/scripts/lib/pull.mjs +82 -0
  51. package/skills/catalyst-github/scripts/read-pr.mjs +97 -0
  52. package/skills/catalyst-linear/SKILL.md +43 -0
  53. package/skills/catalyst-linear/agents/openai.yaml +6 -0
  54. package/skills/catalyst-linear/agents/portability.yaml +5 -0
  55. package/skills/catalyst-linear/references/reading-a-ticket.md +52 -0
  56. package/skills/catalyst-linear/references/what-a-ticket-accumulates.md +53 -0
  57. package/skills/catalyst-linear/references/writing-to-linear.md +43 -0
  58. package/skills/catalyst-linear/scripts/comment.mjs +59 -0
  59. package/skills/catalyst-linear/scripts/create-ticket.mjs +44 -0
  60. package/skills/catalyst-linear/scripts/label.mjs +48 -0
  61. package/skills/catalyst-linear/scripts/lib/cli.mjs +164 -0
  62. package/skills/catalyst-linear/scripts/lib/credential.mjs +29 -0
  63. package/skills/catalyst-linear/scripts/move.mjs +41 -0
  64. package/skills/catalyst-linear/scripts/read-ticket.mjs +93 -0
  65. package/skills/catalyst-linear/scripts/search.mjs +49 -0
  66. package/skills/catalyst-onboard/SKILL.md +57 -0
  67. package/skills/catalyst-onboard/agents/openai.yaml +6 -0
  68. package/skills/catalyst-onboard/agents/portability.yaml +5 -0
  69. package/skills/catalyst-onboard/references/declaring-a-repository.md +23 -0
  70. package/skills/catalyst-onboard/references/skill-sources.md +35 -0
  71. package/skills/catalyst-onboard/references/the-one-path.md +149 -0
  72. package/skills/catalyst-onboard/references/what-a-phase-needs.md +46 -0
  73. package/skills/catalyst-onboard/references/what-the-browser-owns.md +50 -0
  74. package/skills/catalyst-onboard/references/who-fixes-what.md +44 -0
  75. package/skills/catalyst-onboard/scripts/lib/cli.mjs +117 -0
  76. package/skills/catalyst-onboard/scripts/lib/credential.mjs +29 -0
  77. package/skills/catalyst-onboard/scripts/where-am-i.mjs +345 -0
  78. package/skills/catalyst-setup/SKILL.md +36 -0
  79. package/skills/catalyst-setup/agents/openai.yaml +6 -0
  80. package/skills/catalyst-setup/agents/portability.yaml +4 -0
  81. package/skills/catalyst-setup/references/what-each-check-means.md +88 -0
  82. package/skills/catalyst-setup/scripts/check.mjs +75 -0
  83. package/skills/catalyst-setup/scripts/lib/cli.mjs +103 -0
  84. package/skills/catalyst-setup/scripts/lib/credential.mjs +29 -0
  85. package/skills/catalyst-setup/scripts/replica-status.mjs +46 -0
  86. package/skills/connect-me/SKILL.md +63 -0
  87. package/skills/connect-me/agents/openai.yaml +6 -0
  88. package/skills/connect-me/agents/portability.yaml +5 -0
  89. package/skills/connect-me/references/keeping-the-replica-running.md +88 -0
  90. package/skills/connect-me/scripts/lib/cli.mjs +185 -0
  91. package/skills/connect-me/scripts/lib/credential.mjs +29 -0
  92. package/skills/connect-me/scripts/verify-connection.mjs +68 -0
  93. package/skills/how-catalyst-works/SKILL.md +43 -0
  94. package/skills/how-catalyst-works/agents/openai.yaml +6 -0
  95. package/skills/how-catalyst-works/agents/portability.yaml +4 -0
  96. package/skills/how-catalyst-works/references/coding-accounts.md +51 -0
  97. package/skills/how-catalyst-works/references/stages-and-mapping.md +56 -0
  98. package/skills/how-catalyst-works/references/the-ladder.md +41 -0
  99. package/skills/how-catalyst-works/references/what-catalyst-is.md +30 -0
  100. package/skills/how-catalyst-works/references/what-runs-next.md +77 -0
  101. package/skills/how-catalyst-works/references/when-a-phase-fails.md +57 -0
  102. package/skills/how-catalyst-works/scripts/explain-ticket.mjs +41 -0
  103. package/skills/how-catalyst-works/scripts/lib/cli.mjs +164 -0
  104. package/skills/how-catalyst-works/scripts/lib/credential.mjs +29 -0
  105. package/skills/how-catalyst-works/scripts/show-my-map.mjs +94 -0
  106. package/skills/how-catalyst-works/scripts/whats-running.mjs +65 -0
  107. package/skills/run-this-project/SKILL.md +45 -0
  108. package/skills/run-this-project/agents/openai.yaml +6 -0
  109. package/skills/run-this-project/agents/portability.yaml +5 -0
  110. package/skills/run-this-project/assets/stall-policy.json +15 -0
  111. package/skills/run-this-project/references/making-work-ready.md +60 -0
  112. package/skills/run-this-project/references/reacting-to-events.md +76 -0
  113. package/skills/run-this-project/references/stalls-and-escalation.md +63 -0
  114. package/skills/run-this-project/scripts/lib/cli.mjs +185 -0
  115. package/skills/run-this-project/scripts/lib/credential.mjs +29 -0
  116. package/skills/run-this-project/scripts/make-ready.mjs +64 -0
  117. package/skills/run-this-project/scripts/scope-status.mjs +0 -0
  118. package/skills/run-this-project/scripts/watch-scope.mjs +61 -0
  119. package/skills/unstick/SKILL.md +41 -0
  120. package/skills/unstick/agents/openai.yaml +6 -0
  121. package/skills/unstick/agents/portability.yaml +5 -0
  122. package/skills/unstick/references/playbook.md +51 -0
  123. package/skills/unstick/scripts/lib/cli.mjs +135 -0
  124. package/skills/unstick/scripts/lib/credential.mjs +29 -0
  125. package/skills/unstick/scripts/unstick.mjs +57 -0
  126. package/skills/what-needs-me/SKILL.md +41 -0
  127. package/skills/what-needs-me/agents/openai.yaml +6 -0
  128. package/skills/what-needs-me/agents/portability.yaml +5 -0
  129. package/skills/what-needs-me/references/raising-a-decision.md +41 -0
  130. package/skills/what-needs-me/references/reading-the-inbox.md +38 -0
  131. package/skills/what-needs-me/references/settling-an-answer.md +37 -0
  132. package/skills/what-needs-me/scripts/inbox.mjs +56 -0
  133. package/skills/what-needs-me/scripts/lib/cli.mjs +135 -0
  134. package/skills/what-needs-me/scripts/lib/credential.mjs +29 -0
  135. package/skills/what-needs-me/scripts/raise.mjs +53 -0
  136. package/skills/what-needs-me/scripts/settle.mjs +73 -0
  137. package/skills/whats-happening/SKILL.md +43 -0
  138. package/skills/whats-happening/agents/openai.yaml +6 -0
  139. package/skills/whats-happening/agents/portability.yaml +4 -0
  140. package/skills/whats-happening/assets/status-reply.json +77 -0
  141. package/skills/whats-happening/references/reading-the-board.md +43 -0
  142. package/skills/whats-happening/references/reprioritising.md +37 -0
  143. package/skills/whats-happening/references/routing-work.md +36 -0
  144. package/skills/whats-happening/references/status-reply.md +34 -0
  145. package/skills/whats-happening/references/why-is-it-stuck.md +62 -0
  146. package/skills/whats-happening/scripts/explain.mjs +28 -0
  147. package/skills/whats-happening/scripts/lib/cli.mjs +135 -0
  148. package/skills/whats-happening/scripts/lib/credential.mjs +29 -0
  149. package/skills/whats-happening/scripts/snapshot.mjs +149 -0
  150. package/vendor/README.md +9 -0
  151. package/vendor/paths/index.d.ts +85 -0
  152. package/vendor/paths/index.js +148 -0
  153. package/vendor/paths/legacy-installer.d.ts +36 -0
  154. package/vendor/paths/legacy-installer.js +154 -0
  155. package/vendor/paths/node.d.ts +18 -0
  156. package/vendor/paths/node.js +102 -0
  157. package/vendor/paths/provenance.json +17 -0
@@ -0,0 +1,154 @@
1
+ /**
2
+ * The ten roles, in CTC-2548's own order. `artifacts` is last-but-one and is the one role with
3
+ * `base: "none"` — it is minted per run by the caller (`CATALYST_ARTIFACT_DIR`) and this script
4
+ * must never invent a default for it.
5
+ */
6
+ export const CATALYST_DIRECTORY_ROLES = [
7
+ {
8
+ role: "repoRoot",
9
+ variable: "CATALYST_REPO_ROOT",
10
+ base: "catalyst-home",
11
+ segments: ["repos"],
12
+ creatable: true,
13
+ mode: "0755",
14
+ },
15
+ {
16
+ role: "worktrees",
17
+ variable: "CATALYST_WORKTREES_DIR",
18
+ base: "catalyst-home",
19
+ segments: ["wt"],
20
+ creatable: true,
21
+ mode: "0755",
22
+ },
23
+ {
24
+ role: "logs",
25
+ variable: "CATALYST_LOGS_DIR",
26
+ base: "xdg-state",
27
+ segments: ["catalyst", "logs"],
28
+ creatable: true,
29
+ mode: "0755",
30
+ },
31
+ {
32
+ role: "events",
33
+ variable: "CATALYST_EVENTS_DIR",
34
+ base: "xdg-state",
35
+ segments: ["catalyst", "events"],
36
+ creatable: true,
37
+ mode: "0755",
38
+ },
39
+ {
40
+ role: "replica",
41
+ variable: "CATALYST_REPLICA_DIR",
42
+ base: "xdg-state",
43
+ segments: ["catalyst", "replica"],
44
+ creatable: true,
45
+ mode: "0700",
46
+ },
47
+ {
48
+ role: "config",
49
+ variable: "CATALYST_CONFIG_DIR",
50
+ base: "xdg-config",
51
+ segments: ["catalyst"],
52
+ creatable: true,
53
+ mode: "0700",
54
+ },
55
+ {
56
+ role: "cache",
57
+ variable: "CATALYST_CACHE_DIR",
58
+ base: "xdg-cache",
59
+ segments: ["catalyst"],
60
+ creatable: true,
61
+ mode: "0755",
62
+ },
63
+ {
64
+ role: "state",
65
+ variable: "CATALYST_STATE_DIR",
66
+ base: "xdg-state",
67
+ segments: ["catalyst"],
68
+ creatable: true,
69
+ mode: "0755",
70
+ },
71
+ {
72
+ role: "artifacts",
73
+ variable: "CATALYST_ARTIFACT_DIR",
74
+ base: "none",
75
+ segments: [],
76
+ creatable: false,
77
+ mode: "0700",
78
+ },
79
+ {
80
+ role: "skills",
81
+ variable: "CATALYST_SKILLS_HOME",
82
+ base: "xdg-data",
83
+ segments: ["catalyst"],
84
+ creatable: true,
85
+ mode: "0755",
86
+ },
87
+ ];
88
+ /** The roles setup actually creates, in table order. Never hand-counted (CTC-2552 was exactly that bug). */
89
+ export const CREATABLE_DIRECTORY_ROLES = CATALYST_DIRECTORY_ROLES.filter((r) => r.creatable);
90
+ /** The shell (and TypeScript) expression for a base's default, before segments are appended. */
91
+ function baseDefault(base, env) {
92
+ switch (base) {
93
+ case "catalyst-home":
94
+ if (env.CATALYST_HOME)
95
+ return env.CATALYST_HOME;
96
+ return env.HOME ? `${env.HOME}/catalyst` : undefined;
97
+ case "xdg-state":
98
+ if (env.XDG_STATE_HOME)
99
+ return env.XDG_STATE_HOME;
100
+ return env.HOME ? `${env.HOME}/.local/state` : undefined;
101
+ case "xdg-config":
102
+ if (env.XDG_CONFIG_HOME)
103
+ return env.XDG_CONFIG_HOME;
104
+ return env.HOME ? `${env.HOME}/.config` : undefined;
105
+ case "xdg-cache":
106
+ if (env.XDG_CACHE_HOME)
107
+ return env.XDG_CACHE_HOME;
108
+ return env.HOME ? `${env.HOME}/.cache` : undefined;
109
+ case "xdg-data":
110
+ if (env.XDG_DATA_HOME)
111
+ return env.XDG_DATA_HOME;
112
+ return env.HOME ? `${env.HOME}/.local/share` : undefined;
113
+ case "none":
114
+ return undefined;
115
+ }
116
+ }
117
+ /** The shell expression for a base's default (mirrors {@link baseDefault}, rendered instead of resolved). */
118
+ export const LEGACY_BASE_SHELL = {
119
+ "catalyst-home": "${CATALYST_HOME:-$HOME/catalyst}",
120
+ "xdg-state": "${XDG_STATE_HOME:-$HOME/.local/state}",
121
+ "xdg-config": "${XDG_CONFIG_HOME:-$HOME/.config}",
122
+ "xdg-cache": "${XDG_CACHE_HOME:-$HOME/.cache}",
123
+ "xdg-data": "${XDG_DATA_HOME:-$HOME/.local/share}",
124
+ none: null,
125
+ };
126
+ function findRole(role) {
127
+ const found = CATALYST_DIRECTORY_ROLES.find((r) => r.role === role);
128
+ if (!found)
129
+ throw new Error(`catalyst-directories: unknown role "${role}"`);
130
+ return found;
131
+ }
132
+ /**
133
+ * Resolve one role's directory from `env`, the same precedence a shell caller gets from the
134
+ * rendered block: explicit variable → base default → a NAMED refusal, never a relative guess
135
+ * (the house shape: apps/host-sync/src/cli.ts:801-812).
136
+ */
137
+ export function resolveWorkstationDir(role, env) {
138
+ const def = findRole(role);
139
+ const explicit = env[def.variable];
140
+ if (explicit)
141
+ return explicit;
142
+ if (def.base === "none") {
143
+ return {
144
+ refused: `${def.variable} is not set — this directory is minted per run, it has no default`,
145
+ };
146
+ }
147
+ const base = baseDefault(def.base, env);
148
+ if (base === undefined) {
149
+ return {
150
+ refused: `HOME is not set and ${def.variable} is not set — cannot resolve a default for "${role}"`,
151
+ };
152
+ }
153
+ return def.segments.length > 0 ? `${base}/${def.segments.join("/")}` : base;
154
+ }
@@ -0,0 +1,18 @@
1
+ import { type Environment, type MachinePaths, type MachineRole, type Profile } from "./index.js";
2
+ export interface MachineFileOptions {
3
+ readonly env: Environment;
4
+ readonly profile?: Profile;
5
+ readonly file?: string;
6
+ }
7
+ export declare function machinePathsFile(options: MachineFileOptions): string | undefined;
8
+ export declare function loadMachinePaths(options: MachineFileOptions): Promise<MachinePaths | undefined>;
9
+ /** Atomic replacement. Existing parent permissions are never modified. */
10
+ export declare function writeMachinePaths(file: string, record: MachinePaths): Promise<void>;
11
+ export interface LegacyDiscoveryOptions {
12
+ readonly env: Environment;
13
+ readonly replicaDb?: string;
14
+ /** Known file candidates supplied by setup; directories and empty files are never adopted. */
15
+ readonly replicaCandidates?: readonly string[];
16
+ }
17
+ /** Discover legacy CLI locations using metadata only; never touch DB/cursor/lock contents. */
18
+ export declare function discoverLegacyPaths(options: LegacyDiscoveryOptions): Promise<Partial<Record<MachineRole, string>>>;
@@ -0,0 +1,102 @@
1
+ /** Node-only machine-file adapter. No replica database is opened or moved here. */
2
+ import fs from "node:fs/promises";
3
+ import { randomUUID } from "node:crypto";
4
+ import { dirname, join } from "node:path";
5
+ import { absolutePath, parseMachinePaths, } from "./index.js";
6
+ export function machinePathsFile(options) {
7
+ const explicit = options.file ?? options.env.CATALYST_PATHS_FILE;
8
+ if (explicit !== undefined)
9
+ return absolutePath(explicit, "CATALYST_PATHS_FILE");
10
+ if (options.profile === "container")
11
+ return undefined;
12
+ const config = options.env.XDG_CONFIG_HOME ?? `${absolutePath(options.env.HOME, "HOME")}/.config`;
13
+ return join(absolutePath(config, "XDG_CONFIG_HOME"), "catalyst/paths.json");
14
+ }
15
+ function hasCode(error, code) {
16
+ return error instanceof Error && "code" in error && error.code === code;
17
+ }
18
+ async function present(path) {
19
+ try {
20
+ await fs.lstat(path);
21
+ return true;
22
+ }
23
+ catch (error) {
24
+ if (hasCode(error, "ENOENT"))
25
+ return false;
26
+ throw error;
27
+ }
28
+ }
29
+ export async function loadMachinePaths(options) {
30
+ const file = machinePathsFile(options);
31
+ if (file === undefined)
32
+ return undefined;
33
+ try {
34
+ // Only an absent implicit file is unconfigured. Broken links and explicit missing files fail.
35
+ if (options.file === undefined &&
36
+ options.env.CATALYST_PATHS_FILE === undefined &&
37
+ !(await present(file)))
38
+ return undefined;
39
+ return parseMachinePaths(JSON.parse(await fs.readFile(file, "utf8")));
40
+ }
41
+ catch (cause) {
42
+ throw new Error(`paths: cannot read machine file ${file}: ${cause instanceof Error ? cause.message : String(cause)}`, { cause });
43
+ }
44
+ }
45
+ /** Atomic replacement. Existing parent permissions are never modified. */
46
+ export async function writeMachinePaths(file, record) {
47
+ absolutePath(file, "CATALYST_PATHS_FILE");
48
+ const content = `${JSON.stringify(parseMachinePaths(record), null, 2)}\n`;
49
+ await fs.mkdir(dirname(file), { recursive: true, mode: 0o700 });
50
+ const temporary = `${file}.${randomUUID()}.tmp`;
51
+ const handle = await fs.open(temporary, "wx", 0o600);
52
+ try {
53
+ await handle.writeFile(content, "utf8");
54
+ await handle.sync();
55
+ await handle.close();
56
+ await fs.rename(temporary, file);
57
+ }
58
+ finally {
59
+ await handle.close();
60
+ await fs.rm(temporary, { force: true });
61
+ }
62
+ }
63
+ /** Discover legacy CLI locations using metadata only; never touch DB/cursor/lock contents. */
64
+ export async function discoverLegacyPaths(options) {
65
+ const config = absolutePath(options.env.CATALYST_CONFIG_DIR ??
66
+ join(absolutePath(options.env.CATALYST_SKILLS_HOME ?? options.env.HOME, "HOME or deprecated CATALYST_SKILLS_HOME"), ".config/catalyst-cloud"), "CATALYST_CONFIG_DIR");
67
+ const customerFile = join(config, "customer.json");
68
+ const found = {};
69
+ if (await present(config)) {
70
+ if (!(await fs.stat(config)).isDirectory())
71
+ throw new Error(`paths: legacy config is not a directory: ${config}`);
72
+ found.config = config;
73
+ }
74
+ let configured;
75
+ if (await present(customerFile)) {
76
+ const customer = JSON.parse(await fs.readFile(customerFile, "utf8"));
77
+ if (typeof customer !== "object" || customer === null || Array.isArray(customer))
78
+ throw new Error(`paths: invalid legacy config ${customerFile}`);
79
+ if ("replicaDb" in customer && customer.replicaDb !== undefined)
80
+ configured = absolutePath(customer.replicaDb, "legacy replicaDb");
81
+ found.config = config;
82
+ }
83
+ const explicit = options.replicaDb ?? options.env.CATALYST_REPLICA_DB ?? configured;
84
+ if (explicit !== undefined) {
85
+ found.replicaDb = absolutePath(explicit, "CATALYST_REPLICA_DB");
86
+ return found;
87
+ }
88
+ const fallback = join(config, "replica.db");
89
+ const populated = [];
90
+ for (const candidate of new Set([fallback, ...(options.replicaCandidates ?? [])])) {
91
+ absolutePath(candidate, "replica candidate");
92
+ if (!(await present(candidate)))
93
+ continue;
94
+ const stat = await fs.stat(candidate);
95
+ if (stat.isFile() && stat.size > 0)
96
+ populated.push(candidate);
97
+ }
98
+ if (populated.length > 1)
99
+ throw new Error(`paths: ambiguous populated replicas: ${populated.join(", ")}; set CATALYST_REPLICA_DB explicitly`);
100
+ found.replicaDb = populated[0] ?? fallback;
101
+ return found;
102
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "repository": "coalesce-labs/catalyst-cloud",
3
+ "commit": "c391358d06720985126c15b7fa9d9d6ad1878211",
4
+ "sourceSha256": {
5
+ "index.ts": "f44b08a7de1c72d20b77f0c79a1b54b9485c6b0e76eecca298c78f170d7773cd",
6
+ "node.ts": "eb8ad2be272c1d9cf8f2b754e10c89bee237260f8ed0769e612e17a6a94517c5",
7
+ "legacy-installer.ts": "7da0a9c78d44b48e69a0e0ed9c2059d79391b2a519549fff824aaa8130924c2e"
8
+ },
9
+ "runtimeSha256": {
10
+ "index.d.ts": "02fdf46bf7bf39acf01fde211655d0c60e5cc529ec7964bbb9a8876270fc8f19",
11
+ "index.js": "9ed35d5cfd765383584047ed408746e0154209a627861d252729dce741ef0830",
12
+ "legacy-installer.d.ts": "e712488bc7e7aac77e47bbf89e31b6bdcdda9a61c7e81b8a424eb234ac0bb7c3",
13
+ "legacy-installer.js": "78e198e4d6b8b8c8998b18b10d52481b1b5507f8e0f3de6d1049f9b0f9a4b6d9",
14
+ "node.d.ts": "c931a7b077f5b87ed6f4520debf8d584dae7cfb7f15ee9a38db9100870ef5c4b",
15
+ "node.js": "f68dfba24f86b492249f2cfce4a45e72091338be32046c9d8abd246a3c916f98"
16
+ }
17
+ }