@pd4castr/cli 0.0.3 → 0.0.5
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/dist/index.js +429 -423
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -45,97 +45,271 @@ var __callDispose = (stack, error, hasError) => {
|
|
|
45
45
|
return next();
|
|
46
46
|
};
|
|
47
47
|
|
|
48
|
-
// src/
|
|
49
|
-
|
|
48
|
+
// src/constants.ts
|
|
49
|
+
var AUTH0_DOMAIN = "pdview.au.auth0.com";
|
|
50
|
+
var AUTH0_CLIENT_ID = "Q5tQNF57cQlVXnVsqnU0hhgy92rVb03W";
|
|
51
|
+
var AUTH0_AUDIENCE = "https://api.pd4castr.com.au";
|
|
52
|
+
var GLOBAL_CONFIG_FILE = ".pd4castr";
|
|
53
|
+
var PROJECT_CONFIG_FILE = ".pd4castrrc.json";
|
|
54
|
+
var API_URL = "https://pd4castr-api.pipelabs.app";
|
|
55
|
+
var TEST_INPUT_DATA_DIR = "test_input";
|
|
56
|
+
var TEST_OUTPUT_DATA_DIR = "test_output";
|
|
57
|
+
var TEST_OUTPUT_FILENAME = `output-${Date.now()}.json`;
|
|
50
58
|
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
|
|
59
|
+
// src/commands/fetch/handle-action.ts
|
|
60
|
+
import path5 from "path";
|
|
61
|
+
import ora from "ora";
|
|
62
|
+
import { ZodError } from "zod";
|
|
63
|
+
|
|
64
|
+
// src/config/load-project-config.ts
|
|
65
|
+
import path from "path";
|
|
66
|
+
import { lilconfig } from "lilconfig";
|
|
67
|
+
|
|
68
|
+
// src/schemas/project-config-schema.ts
|
|
69
|
+
import { z } from "zod";
|
|
70
|
+
var aemoDataFetcherSchema = z.object({
|
|
71
|
+
type: z.literal("AEMO_MMS"),
|
|
72
|
+
checkInterval: z.number().int().min(60),
|
|
73
|
+
config: z.object({
|
|
74
|
+
checkQuery: z.string(),
|
|
75
|
+
fetchQuery: z.string()
|
|
76
|
+
})
|
|
77
|
+
});
|
|
78
|
+
var dataFetcherSchema = z.discriminatedUnion("type", [aemoDataFetcherSchema]);
|
|
79
|
+
var modelInputSchema = z.object({
|
|
80
|
+
key: z.string(),
|
|
81
|
+
inputSource: z.string().optional(),
|
|
82
|
+
trigger: z.enum(["WAIT_FOR_LATEST_FILE", "USE_MOST_RECENT_FILE"]),
|
|
83
|
+
fetcher: dataFetcherSchema.optional()
|
|
84
|
+
});
|
|
85
|
+
var modelOutputSchema = z.object({
|
|
86
|
+
name: z.string(),
|
|
87
|
+
seriesKey: z.boolean(),
|
|
88
|
+
colour: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional()
|
|
89
|
+
});
|
|
90
|
+
var projectConfigSchema = z.object({
|
|
91
|
+
$$id: z.string().nullable(),
|
|
92
|
+
$$modelGroupID: z.string().nullable(),
|
|
93
|
+
$$revision: z.number().int().min(0).default(0),
|
|
94
|
+
$$dockerImage: z.string().nullable(),
|
|
95
|
+
name: z.string(),
|
|
96
|
+
metadata: z.record(z.string(), z.any()).optional(),
|
|
97
|
+
forecastVariable: z.enum(["PRICE"]),
|
|
98
|
+
timeHorizon: z.enum(["ACTUAL", "DAY_AHEAD", "WEEK_AHEAD"]),
|
|
99
|
+
inputs: z.array(modelInputSchema),
|
|
100
|
+
outputs: z.array(modelOutputSchema)
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// src/config/parse-project-config.ts
|
|
104
|
+
function parseProjectConfig(config) {
|
|
105
|
+
return projectConfigSchema.parse(config);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// src/config/load-project-config.ts
|
|
109
|
+
async function loadProjectConfig() {
|
|
110
|
+
const result = await lilconfig("pd4castr", {
|
|
111
|
+
searchPlaces: [PROJECT_CONFIG_FILE]
|
|
112
|
+
}).search();
|
|
113
|
+
if (!result?.config) {
|
|
114
|
+
throw new Error(
|
|
115
|
+
"No config found (docs: https://github.com/pipelabs/pd4castr-model-examples/blob/main/docs/005-config.md)."
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
const config = parseProjectConfig(result.config);
|
|
119
|
+
const projectRoot = path.dirname(result.filepath);
|
|
120
|
+
return {
|
|
121
|
+
config,
|
|
122
|
+
projectRoot
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/utils/create-link.ts
|
|
127
|
+
var ESC = "\x1B";
|
|
128
|
+
var OSC = `${ESC}]`;
|
|
129
|
+
var SEP = ";";
|
|
130
|
+
function createLink(text, url) {
|
|
131
|
+
const start = `${OSC}8${SEP}${SEP}${url}${ESC}\\`;
|
|
132
|
+
const end = `${OSC}8${SEP}${SEP}${ESC}\\`;
|
|
133
|
+
return `${start}${text}${end}`;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// src/utils/get-auth.ts
|
|
137
|
+
import invariant from "tiny-invariant";
|
|
138
|
+
|
|
139
|
+
// src/commands/login/utils/is-authed.ts
|
|
140
|
+
function isAuthed(config) {
|
|
141
|
+
const isTokenExpired = config.accessTokenExpiresAt && config.accessTokenExpiresAt <= Date.now();
|
|
142
|
+
return Boolean(config.accessToken) && !isTokenExpired;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// src/config/load-global-config.ts
|
|
146
|
+
import fs2 from "fs/promises";
|
|
147
|
+
import os from "os";
|
|
148
|
+
import path2 from "path";
|
|
149
|
+
|
|
150
|
+
// src/schemas/global-config-schema.ts
|
|
151
|
+
import { z as z2 } from "zod";
|
|
152
|
+
var globalConfigSchema = z2.object({
|
|
153
|
+
accessToken: z2.string().optional(),
|
|
154
|
+
accessTokenExpiresAt: z2.number().optional()
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// src/utils/is-existing-path.ts
|
|
158
|
+
import fs from "fs/promises";
|
|
159
|
+
async function isExistingPath(path12) {
|
|
160
|
+
try {
|
|
161
|
+
await fs.access(path12);
|
|
162
|
+
return true;
|
|
163
|
+
} catch {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// src/config/load-global-config.ts
|
|
169
|
+
async function loadGlobalConfig() {
|
|
170
|
+
const configPath = path2.join(os.homedir(), GLOBAL_CONFIG_FILE);
|
|
171
|
+
const configExists = await isExistingPath(configPath);
|
|
172
|
+
if (!configExists) {
|
|
173
|
+
await fs2.writeFile(configPath, JSON.stringify({}));
|
|
174
|
+
return {};
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
const configFileContents = await fs2.readFile(configPath, "utf8");
|
|
178
|
+
const config = JSON.parse(configFileContents);
|
|
179
|
+
return globalConfigSchema.parse(config);
|
|
180
|
+
} catch {
|
|
181
|
+
return {};
|
|
127
182
|
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// src/utils/get-auth.ts
|
|
186
|
+
async function getAuth() {
|
|
187
|
+
const config = await loadGlobalConfig();
|
|
188
|
+
if (!isAuthed(config)) {
|
|
189
|
+
throw new Error("Not authenticated. Please run `pd4castr login` to login.");
|
|
190
|
+
}
|
|
191
|
+
invariant(config.accessToken, "Access token is required");
|
|
192
|
+
invariant(config.accessTokenExpiresAt, "Access token expiry is required");
|
|
193
|
+
return {
|
|
194
|
+
accessToken: config.accessToken,
|
|
195
|
+
expiresAt: config.accessTokenExpiresAt
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// src/utils/log-zod-issues.ts
|
|
200
|
+
function logZodIssues(error) {
|
|
201
|
+
for (const issue of error.issues) {
|
|
202
|
+
console.log(` \u2718 ${issue.path.join(".")} - ${issue.message}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// src/commands/fetch/utils/fetch-aemo-data.ts
|
|
207
|
+
import fs3 from "fs/promises";
|
|
208
|
+
import path3 from "path";
|
|
209
|
+
|
|
210
|
+
// src/api.ts
|
|
211
|
+
import ky from "ky";
|
|
212
|
+
var api = ky.create({
|
|
213
|
+
prefixUrl: process.env.PD4CASTR_API_URL ?? API_URL
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// src/commands/fetch/utils/fetch-aemo-data.ts
|
|
217
|
+
async function fetchAEMOData(dataFetcher, authCtx, ctx) {
|
|
218
|
+
const queryPath = path3.resolve(
|
|
219
|
+
ctx.projectRoot,
|
|
220
|
+
dataFetcher.config.fetchQuery
|
|
221
|
+
);
|
|
222
|
+
const querySQL = await fs3.readFile(queryPath, "utf8");
|
|
223
|
+
const headers = { Authorization: `Bearer ${authCtx.accessToken}` };
|
|
224
|
+
const payload2 = { query: querySQL, type: "AEMO_MMS" };
|
|
225
|
+
const result = await api.post("data-fetcher/query", { json: payload2, headers }).json();
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// src/commands/fetch/utils/get-fetcher.ts
|
|
230
|
+
var DATA_FETCHER_FNS = {
|
|
231
|
+
AEMO_MMS: fetchAEMOData
|
|
128
232
|
};
|
|
233
|
+
function getFetcher(type) {
|
|
234
|
+
const fetcher = DATA_FETCHER_FNS[type];
|
|
235
|
+
if (!fetcher) {
|
|
236
|
+
throw new Error(`Unsupported data fetcher type: ${type}`);
|
|
237
|
+
}
|
|
238
|
+
return fetcher;
|
|
239
|
+
}
|
|
129
240
|
|
|
130
|
-
// src/
|
|
131
|
-
|
|
132
|
-
|
|
241
|
+
// src/commands/fetch/utils/write-test-data.ts
|
|
242
|
+
import fs4 from "fs/promises";
|
|
243
|
+
import path4 from "path";
|
|
244
|
+
|
|
245
|
+
// src/utils/get-input-filename.ts
|
|
246
|
+
function getInputFilename(modelInput) {
|
|
247
|
+
return `${modelInput.key}.json`;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// src/commands/fetch/utils/write-test-data.ts
|
|
251
|
+
async function writeTestData(inputData, modelInput, inputDataDir, ctx) {
|
|
252
|
+
const inputDir = path4.resolve(ctx.projectRoot, inputDataDir);
|
|
253
|
+
await fs4.mkdir(inputDir, { recursive: true });
|
|
254
|
+
const inputFilename = getInputFilename(modelInput);
|
|
255
|
+
const inputPath = path4.resolve(inputDir, inputFilename);
|
|
256
|
+
await fs4.writeFile(inputPath, JSON.stringify(inputData, void 0, 2));
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// src/commands/fetch/handle-action.ts
|
|
260
|
+
var FETCHABLE_DATA_FETCHER_TYPES = /* @__PURE__ */ new Set(["AEMO_MMS"]);
|
|
261
|
+
async function handleAction(options) {
|
|
262
|
+
const spinner = ora("Starting data fetch...").start();
|
|
263
|
+
try {
|
|
264
|
+
const authCtx = await getAuth();
|
|
265
|
+
const ctx = await loadProjectConfig();
|
|
266
|
+
for (const input2 of ctx.config.inputs) {
|
|
267
|
+
if (!input2.fetcher) {
|
|
268
|
+
spinner.warn(`\`${input2.key}\` - no data fetcher defined, skipping`);
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
if (!FETCHABLE_DATA_FETCHER_TYPES.has(input2.fetcher.type)) {
|
|
272
|
+
spinner.warn(
|
|
273
|
+
`\`${input2.key}\` (${input2.fetcher.type}) - unsupported, skipping`
|
|
274
|
+
);
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
spinner.start(`\`${input2.key}\` (${input2.fetcher.type}) - fetching...`);
|
|
278
|
+
const fetchData = getFetcher(input2.fetcher.type);
|
|
279
|
+
const output = await fetchData(input2.fetcher, authCtx, ctx);
|
|
280
|
+
await writeTestData(output, input2, options.inputDir, ctx);
|
|
281
|
+
spinner.succeed(`\`${input2.key}\` (${input2.fetcher.type}) - fetched`);
|
|
282
|
+
}
|
|
283
|
+
const inputPath = path5.resolve(ctx.projectRoot, options.inputDir);
|
|
284
|
+
const link = createLink("Click here", `file://${inputPath}`);
|
|
285
|
+
console.log(`
|
|
286
|
+
${link} to view fetched data
|
|
287
|
+
`);
|
|
288
|
+
} catch (error) {
|
|
289
|
+
if (error instanceof ZodError) {
|
|
290
|
+
spinner.fail("Config validation failed");
|
|
291
|
+
logZodIssues(error);
|
|
292
|
+
} else if (error instanceof Error) {
|
|
293
|
+
spinner.fail(error.message);
|
|
294
|
+
}
|
|
295
|
+
process.exit(1);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// src/commands/fetch/index.ts
|
|
300
|
+
function registerFetchCommand(program2) {
|
|
301
|
+
program2.command("fetch").description("Fetches test data from configured data fetchers.").option(
|
|
302
|
+
"-i, --input-dir <path>",
|
|
303
|
+
"The input test data directory",
|
|
304
|
+
TEST_INPUT_DATA_DIR
|
|
305
|
+
).action(handleAction);
|
|
306
|
+
}
|
|
133
307
|
|
|
134
308
|
// src/commands/init/handle-action.ts
|
|
135
|
-
import
|
|
309
|
+
import path6 from "path";
|
|
136
310
|
import * as inquirer from "@inquirer/prompts";
|
|
311
|
+
import ora2 from "ora";
|
|
137
312
|
import tiged from "tiged";
|
|
138
|
-
import ora from "ora";
|
|
139
313
|
|
|
140
314
|
// src/commands/init/constants.ts
|
|
141
315
|
var templates = {
|
|
@@ -156,17 +330,6 @@ function getTemplatePath(template) {
|
|
|
156
330
|
return `https://github.com/${template.repo}/${template.path}`;
|
|
157
331
|
}
|
|
158
332
|
|
|
159
|
-
// src/utils/is-existing-path.ts
|
|
160
|
-
import fs from "fs/promises";
|
|
161
|
-
async function isExistingPath(path12) {
|
|
162
|
-
try {
|
|
163
|
-
await fs.access(path12);
|
|
164
|
-
return true;
|
|
165
|
-
} catch {
|
|
166
|
-
return false;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
333
|
// src/commands/init/utils/validate-name.ts
|
|
171
334
|
async function validateName(value) {
|
|
172
335
|
const exists = await isExistingPath(`./${value}`);
|
|
@@ -177,7 +340,7 @@ async function validateName(value) {
|
|
|
177
340
|
}
|
|
178
341
|
|
|
179
342
|
// src/commands/init/handle-action.ts
|
|
180
|
-
async function
|
|
343
|
+
async function handleAction2() {
|
|
181
344
|
const projectName = await inquirer.input({
|
|
182
345
|
message: "Name your new model project",
|
|
183
346
|
default: "my-model",
|
|
@@ -190,7 +353,7 @@ async function handleAction() {
|
|
|
190
353
|
value: template2.name
|
|
191
354
|
}))
|
|
192
355
|
});
|
|
193
|
-
const spinner =
|
|
356
|
+
const spinner = ora2("Fetching template...").start();
|
|
194
357
|
try {
|
|
195
358
|
await fetchTemplate(template, projectName);
|
|
196
359
|
spinner.succeed("Template fetched successfully");
|
|
@@ -208,104 +371,36 @@ async function fetchTemplate(template, projectName) {
|
|
|
208
371
|
disableCache: true,
|
|
209
372
|
force: true
|
|
210
373
|
});
|
|
211
|
-
const destination =
|
|
374
|
+
const destination = path6.join(process.cwd(), projectName);
|
|
212
375
|
await fetcher.clone(destination);
|
|
213
376
|
}
|
|
214
377
|
|
|
215
378
|
// src/commands/init/index.ts
|
|
216
379
|
function registerInitCommand(program2) {
|
|
217
|
-
program2.command("init").description("Initialize a new model using a template.").action(
|
|
380
|
+
program2.command("init").description("Initialize a new model using a template.").action(handleAction2);
|
|
218
381
|
}
|
|
219
382
|
|
|
220
383
|
// src/commands/login/handle-action.ts
|
|
221
|
-
import
|
|
222
|
-
import { ZodError } from "zod";
|
|
223
|
-
|
|
224
|
-
// src/config/load-global-config.ts
|
|
225
|
-
import fs2 from "fs/promises";
|
|
226
|
-
import path2 from "path";
|
|
227
|
-
import os from "os";
|
|
228
|
-
|
|
229
|
-
// src/constants.ts
|
|
230
|
-
var AUTH0_DOMAIN = "pdview.au.auth0.com";
|
|
231
|
-
var AUTH0_CLIENT_ID = "Q5tQNF57cQlVXnVsqnU0hhgy92rVb03W";
|
|
232
|
-
var AUTH0_AUDIENCE = "https://api.pd4castr.com.au";
|
|
233
|
-
var GLOBAL_CONFIG_FILE = ".pd4castr";
|
|
234
|
-
var PROJECT_CONFIG_FILE = ".pd4castrrc.json";
|
|
235
|
-
var API_URL = "https://pd4castr-api.pipelabs.app";
|
|
236
|
-
var TEST_INPUT_DATA_DIR = "test_input";
|
|
237
|
-
var TEST_OUTPUT_DATA_DIR = "test_output";
|
|
238
|
-
var TEST_OUTPUT_FILENAME = `output-${Date.now()}.json`;
|
|
239
|
-
|
|
240
|
-
// src/schemas/global-config-schema.ts
|
|
241
|
-
import { z } from "zod";
|
|
242
|
-
var globalConfigSchema = z.object({
|
|
243
|
-
accessToken: z.string().optional(),
|
|
244
|
-
accessTokenExpiresAt: z.number().optional()
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
// src/config/load-global-config.ts
|
|
248
|
-
async function loadGlobalConfig() {
|
|
249
|
-
const configPath = path2.join(os.homedir(), GLOBAL_CONFIG_FILE);
|
|
250
|
-
const configExists = await isExistingPath(configPath);
|
|
251
|
-
if (!configExists) {
|
|
252
|
-
await fs2.writeFile(configPath, JSON.stringify({}));
|
|
253
|
-
return {};
|
|
254
|
-
}
|
|
255
|
-
try {
|
|
256
|
-
const configFileContents = await fs2.readFile(configPath, "utf8");
|
|
257
|
-
const config = JSON.parse(configFileContents);
|
|
258
|
-
return globalConfigSchema.parse(config);
|
|
259
|
-
} catch {
|
|
260
|
-
return {};
|
|
261
|
-
}
|
|
262
|
-
}
|
|
384
|
+
import ora3 from "ora";
|
|
385
|
+
import { ZodError as ZodError2 } from "zod";
|
|
263
386
|
|
|
264
387
|
// src/config/update-global-config.ts
|
|
265
|
-
import
|
|
266
|
-
import path3 from "path";
|
|
388
|
+
import fs5 from "fs/promises";
|
|
267
389
|
import os2 from "os";
|
|
390
|
+
import path7 from "path";
|
|
268
391
|
async function updateGlobalConfig(updatedConfig) {
|
|
269
|
-
const configPath =
|
|
270
|
-
await
|
|
392
|
+
const configPath = path7.join(os2.homedir(), GLOBAL_CONFIG_FILE);
|
|
393
|
+
await fs5.writeFile(configPath, JSON.stringify(updatedConfig, void 0, 2));
|
|
271
394
|
}
|
|
272
395
|
|
|
273
|
-
// src/utils/
|
|
274
|
-
|
|
275
|
-
for (const issue of error.issues) {
|
|
276
|
-
console.log(` \u2718 ${issue.path.join(".")} - ${issue.message}`);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// src/commands/login/utils/is-authed.ts
|
|
281
|
-
function isAuthed(config) {
|
|
282
|
-
const isTokenExpired = config.accessTokenExpiresAt && config.accessTokenExpiresAt <= Date.now();
|
|
283
|
-
return Boolean(config.accessToken) && !isTokenExpired;
|
|
284
|
-
}
|
|
396
|
+
// src/commands/login/utils/complete-auth-flow.ts
|
|
397
|
+
import { HTTPError } from "ky";
|
|
285
398
|
|
|
286
399
|
// src/commands/login/auth0-api.ts
|
|
287
|
-
import
|
|
288
|
-
var auth0API =
|
|
289
|
-
|
|
290
|
-
// src/commands/login/utils/start-auth-flow.ts
|
|
291
|
-
var payload = {
|
|
292
|
-
client_id: AUTH0_CLIENT_ID,
|
|
293
|
-
audience: AUTH0_AUDIENCE,
|
|
294
|
-
scope: "openid profile"
|
|
295
|
-
};
|
|
296
|
-
async function startAuthFlow() {
|
|
297
|
-
const codeResponse = await auth0API.post("oauth/device/code", { json: payload }).json();
|
|
298
|
-
const authContext = {
|
|
299
|
-
deviceCode: codeResponse.device_code,
|
|
300
|
-
verificationURL: codeResponse.verification_uri_complete,
|
|
301
|
-
userCode: codeResponse.user_code,
|
|
302
|
-
checkInterval: codeResponse.interval
|
|
303
|
-
};
|
|
304
|
-
return authContext;
|
|
305
|
-
}
|
|
400
|
+
import ky2 from "ky";
|
|
401
|
+
var auth0API = ky2.create({ prefixUrl: `https://${AUTH0_DOMAIN}` });
|
|
306
402
|
|
|
307
403
|
// src/commands/login/utils/complete-auth-flow.ts
|
|
308
|
-
import { HTTPError } from "ky";
|
|
309
404
|
var FAILED_AUTH_ERRORS = /* @__PURE__ */ new Set(["expired_token", "access_denied"]);
|
|
310
405
|
async function completeAuthFlow(authCtx) {
|
|
311
406
|
const payload2 = {
|
|
@@ -341,9 +436,26 @@ async function completeAuthFlow(authCtx) {
|
|
|
341
436
|
return fetchAuthResponse();
|
|
342
437
|
}
|
|
343
438
|
|
|
439
|
+
// src/commands/login/utils/start-auth-flow.ts
|
|
440
|
+
var payload = {
|
|
441
|
+
client_id: AUTH0_CLIENT_ID,
|
|
442
|
+
audience: AUTH0_AUDIENCE,
|
|
443
|
+
scope: "openid email"
|
|
444
|
+
};
|
|
445
|
+
async function startAuthFlow() {
|
|
446
|
+
const codeResponse = await auth0API.post("oauth/device/code", { json: payload }).json();
|
|
447
|
+
const authContext = {
|
|
448
|
+
deviceCode: codeResponse.device_code,
|
|
449
|
+
verificationURL: codeResponse.verification_uri_complete,
|
|
450
|
+
userCode: codeResponse.user_code,
|
|
451
|
+
checkInterval: codeResponse.interval
|
|
452
|
+
};
|
|
453
|
+
return authContext;
|
|
454
|
+
}
|
|
455
|
+
|
|
344
456
|
// src/commands/login/handle-action.ts
|
|
345
|
-
async function
|
|
346
|
-
const spinner =
|
|
457
|
+
async function handleAction3() {
|
|
458
|
+
const spinner = ora3("Logging in to the pd4castr API...").start();
|
|
347
459
|
try {
|
|
348
460
|
const globalConfig = await loadGlobalConfig();
|
|
349
461
|
if (isAuthed(globalConfig)) {
|
|
@@ -358,111 +470,39 @@ async function handleAction2() {
|
|
|
358
470
|
spinner.info(`Your login code is:
|
|
359
471
|
${authCtx.userCode}
|
|
360
472
|
`);
|
|
361
|
-
spinner.start("Waiting for login to complete...");
|
|
362
|
-
const authPayload = await completeAuthFlow(authCtx);
|
|
363
|
-
const updatedGlobalConfig = {
|
|
364
|
-
...globalConfig,
|
|
365
|
-
accessToken: authPayload.accessToken,
|
|
366
|
-
accessTokenExpiresAt: authPayload.expiresAt
|
|
367
|
-
};
|
|
368
|
-
await updateGlobalConfig(updatedGlobalConfig);
|
|
369
|
-
spinner.succeed("Successfully logged in to the pd4castr API");
|
|
370
|
-
} catch (error) {
|
|
371
|
-
if (error instanceof
|
|
372
|
-
spinner.fail("Config validation failed");
|
|
373
|
-
logZodIssues(error);
|
|
374
|
-
} else if (error instanceof Error) {
|
|
375
|
-
spinner.fail(error.message);
|
|
376
|
-
}
|
|
377
|
-
process.exit(1);
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
// src/commands/login/index.ts
|
|
382
|
-
function registerLoginCommand(program2) {
|
|
383
|
-
program2.command("login").description("Logs in to the pd4castr API.").action(handleAction2);
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
// src/commands/test/handle-action.ts
|
|
387
|
-
import ora3 from "ora";
|
|
388
|
-
import express from "express";
|
|
389
|
-
import path8 from "path";
|
|
390
|
-
import slugify from "slugify";
|
|
391
|
-
import { ExecaError } from "execa";
|
|
392
|
-
import { ZodError as ZodError2 } from "zod";
|
|
393
|
-
|
|
394
|
-
// src/config/load-project-config.ts
|
|
395
|
-
import path4 from "path";
|
|
396
|
-
import { lilconfig } from "lilconfig";
|
|
397
|
-
|
|
398
|
-
// src/schemas/project-config-schema.ts
|
|
399
|
-
import { z as z2 } from "zod";
|
|
400
|
-
var aemoDataFetcherSchema = z2.object({
|
|
401
|
-
type: z2.literal("AEMO_MMS"),
|
|
402
|
-
checkInterval: z2.number().int().min(60),
|
|
403
|
-
config: z2.object({
|
|
404
|
-
checkQuery: z2.string(),
|
|
405
|
-
fetchQuery: z2.string()
|
|
406
|
-
})
|
|
407
|
-
});
|
|
408
|
-
var dataFetcherSchema = z2.discriminatedUnion("type", [aemoDataFetcherSchema]);
|
|
409
|
-
var modelInputSchema = z2.object({
|
|
410
|
-
key: z2.string(),
|
|
411
|
-
inputSource: z2.string().optional(),
|
|
412
|
-
trigger: z2.enum(["WAIT_FOR_LATEST_FILE", "USE_MOST_RECENT_FILE"]),
|
|
413
|
-
fetcher: dataFetcherSchema.optional()
|
|
414
|
-
});
|
|
415
|
-
var modelOutputSchema = z2.object({
|
|
416
|
-
name: z2.string(),
|
|
417
|
-
seriesKey: z2.boolean(),
|
|
418
|
-
colour: z2.string().regex(/^#[0-9A-Fa-f]{6}$/).optional()
|
|
419
|
-
});
|
|
420
|
-
var projectConfigSchema = z2.object({
|
|
421
|
-
$$id: z2.string().nullable(),
|
|
422
|
-
$$modelGroupID: z2.string().nullable(),
|
|
423
|
-
$$revision: z2.number().int().min(0).default(0),
|
|
424
|
-
$$dockerImage: z2.string().nullable(),
|
|
425
|
-
name: z2.string(),
|
|
426
|
-
metadata: z2.record(z2.string(), z2.any()).optional(),
|
|
427
|
-
forecastVariable: z2.enum(["PRICE"]),
|
|
428
|
-
timeHorizon: z2.enum(["ACTUAL", "DAY_AHEAD", "WEEK_AHEAD"]),
|
|
429
|
-
inputs: z2.array(modelInputSchema),
|
|
430
|
-
outputs: z2.array(modelOutputSchema)
|
|
431
|
-
});
|
|
432
|
-
|
|
433
|
-
// src/config/parse-project-config.ts
|
|
434
|
-
function parseProjectConfig(config) {
|
|
435
|
-
return projectConfigSchema.parse(config);
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
// src/config/load-project-config.ts
|
|
439
|
-
async function loadProjectConfig() {
|
|
440
|
-
const result = await lilconfig("pd4castr", {
|
|
441
|
-
searchPlaces: [PROJECT_CONFIG_FILE]
|
|
442
|
-
}).search();
|
|
443
|
-
if (!result?.config) {
|
|
444
|
-
throw new Error(
|
|
445
|
-
"No config found (docs: https://github.com/pipelabs/pd4castr-model-examples/blob/main/docs/005-config.md)."
|
|
446
|
-
);
|
|
473
|
+
spinner.start("Waiting for login to complete...");
|
|
474
|
+
const authPayload = await completeAuthFlow(authCtx);
|
|
475
|
+
const updatedGlobalConfig = {
|
|
476
|
+
...globalConfig,
|
|
477
|
+
accessToken: authPayload.accessToken,
|
|
478
|
+
accessTokenExpiresAt: authPayload.expiresAt
|
|
479
|
+
};
|
|
480
|
+
await updateGlobalConfig(updatedGlobalConfig);
|
|
481
|
+
spinner.succeed("Successfully logged in to the pd4castr API");
|
|
482
|
+
} catch (error) {
|
|
483
|
+
if (error instanceof ZodError2) {
|
|
484
|
+
spinner.fail("Config validation failed");
|
|
485
|
+
logZodIssues(error);
|
|
486
|
+
} else if (error instanceof Error) {
|
|
487
|
+
spinner.fail(error.message);
|
|
488
|
+
}
|
|
489
|
+
process.exit(1);
|
|
447
490
|
}
|
|
448
|
-
const config = parseProjectConfig(result.config);
|
|
449
|
-
const projectRoot = path4.dirname(result.filepath);
|
|
450
|
-
return {
|
|
451
|
-
config,
|
|
452
|
-
projectRoot
|
|
453
|
-
};
|
|
454
491
|
}
|
|
455
492
|
|
|
456
|
-
// src/
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
var SEP = ";";
|
|
460
|
-
function createLink(text, url) {
|
|
461
|
-
const start = `${OSC}8${SEP}${SEP}${url}${ESC}\\`;
|
|
462
|
-
const end = `${OSC}8${SEP}${SEP}${ESC}\\`;
|
|
463
|
-
return `${start}${text}${end}`;
|
|
493
|
+
// src/commands/login/index.ts
|
|
494
|
+
function registerLoginCommand(program2) {
|
|
495
|
+
program2.command("login").description("Logs in to the pd4castr API.").action(handleAction3);
|
|
464
496
|
}
|
|
465
497
|
|
|
498
|
+
// src/commands/test/handle-action.ts
|
|
499
|
+
import path11 from "path";
|
|
500
|
+
import { ExecaError } from "execa";
|
|
501
|
+
import express from "express";
|
|
502
|
+
import ora4 from "ora";
|
|
503
|
+
import slugify from "slugify";
|
|
504
|
+
import { ZodError as ZodError3 } from "zod";
|
|
505
|
+
|
|
466
506
|
// src/commands/test/utils/build-docker-image.ts
|
|
467
507
|
import { execa } from "execa";
|
|
468
508
|
async function buildDockerImage(dockerImage, ctx) {
|
|
@@ -476,15 +516,29 @@ async function buildDockerImage(dockerImage, ctx) {
|
|
|
476
516
|
}
|
|
477
517
|
}
|
|
478
518
|
|
|
519
|
+
// src/commands/test/utils/check-input-files.ts
|
|
520
|
+
import path8 from "path";
|
|
521
|
+
async function checkInputFiles(inputFiles, inputDataPath, ctx) {
|
|
522
|
+
for (const inputFile of inputFiles) {
|
|
523
|
+
const filePath = path8.join(ctx.projectRoot, inputDataPath, inputFile);
|
|
524
|
+
const exists = await isExistingPath(filePath);
|
|
525
|
+
if (!exists) {
|
|
526
|
+
throw new Error(
|
|
527
|
+
`Input data not found (${inputFile}) - did you need to run \`pd4castr fetch\`?`
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
479
533
|
// src/commands/test/utils/create-input-handler.ts
|
|
480
|
-
import
|
|
534
|
+
import path9 from "path";
|
|
481
535
|
function createInputHandler(inputFilesPath, modelIOChecks, ctx) {
|
|
482
536
|
return (req, res) => {
|
|
483
537
|
if (!modelIOChecks.isValidInput(req.params.filename)) {
|
|
484
538
|
return res.status(404).json({ error: "File not found" });
|
|
485
539
|
}
|
|
486
540
|
modelIOChecks.trackInputHandled(req.params.filename);
|
|
487
|
-
const filePath =
|
|
541
|
+
const filePath = path9.join(
|
|
488
542
|
ctx.projectRoot,
|
|
489
543
|
inputFilesPath,
|
|
490
544
|
req.params.filename
|
|
@@ -494,39 +548,20 @@ function createInputHandler(inputFilesPath, modelIOChecks, ctx) {
|
|
|
494
548
|
}
|
|
495
549
|
|
|
496
550
|
// src/commands/test/utils/create-output-handler.ts
|
|
497
|
-
import
|
|
498
|
-
import
|
|
551
|
+
import fs6 from "fs/promises";
|
|
552
|
+
import path10 from "path";
|
|
499
553
|
function createOutputHandler(modelIOChecks, ctx) {
|
|
500
554
|
return async (req, res) => {
|
|
501
555
|
modelIOChecks.trackOutputHandled();
|
|
502
|
-
const outputPath =
|
|
503
|
-
await
|
|
504
|
-
const outputFilePath =
|
|
556
|
+
const outputPath = path10.join(ctx.projectRoot, TEST_OUTPUT_DATA_DIR);
|
|
557
|
+
await fs6.mkdir(outputPath, { recursive: true });
|
|
558
|
+
const outputFilePath = path10.join(outputPath, TEST_OUTPUT_FILENAME);
|
|
505
559
|
const outputData = JSON.stringify(req.body, null, 2);
|
|
506
|
-
await
|
|
560
|
+
await fs6.writeFile(outputFilePath, outputData, "utf8");
|
|
507
561
|
return res.status(200).json({ success: true });
|
|
508
562
|
};
|
|
509
563
|
}
|
|
510
564
|
|
|
511
|
-
// src/commands/test/utils/check-input-files.ts
|
|
512
|
-
import path7 from "path";
|
|
513
|
-
async function checkInputFiles(inputFiles, inputDataPath, ctx) {
|
|
514
|
-
for (const inputFile of inputFiles) {
|
|
515
|
-
const filePath = path7.join(ctx.projectRoot, inputDataPath, inputFile);
|
|
516
|
-
const exists = await isExistingPath(filePath);
|
|
517
|
-
if (!exists) {
|
|
518
|
-
throw new Error(
|
|
519
|
-
`Input data not found (${inputFile}) - did you need to run \`pd4castr fetch\`?`
|
|
520
|
-
);
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
// src/utils/get-input-filename.ts
|
|
526
|
-
function getInputFilename(modelInput) {
|
|
527
|
-
return `${modelInput.key}.json`;
|
|
528
|
-
}
|
|
529
|
-
|
|
530
565
|
// src/commands/test/utils/get-input-files.ts
|
|
531
566
|
function getInputFiles(config) {
|
|
532
567
|
const inputFiles = config.inputs.map((input2) => getInputFilename(input2));
|
|
@@ -588,7 +623,6 @@ async function runModelContainer(dockerImage, webserverURL, ctx) {
|
|
|
588
623
|
const args = [
|
|
589
624
|
"run",
|
|
590
625
|
"--rm",
|
|
591
|
-
"--network=host",
|
|
592
626
|
...envs.flatMap((env) => ["--env", env]),
|
|
593
627
|
dockerImage
|
|
594
628
|
];
|
|
@@ -614,10 +648,10 @@ async function startWebServer(app, port) {
|
|
|
614
648
|
}
|
|
615
649
|
|
|
616
650
|
// src/commands/test/handle-action.ts
|
|
617
|
-
async function
|
|
651
|
+
async function handleAction4(options) {
|
|
618
652
|
var _stack = [];
|
|
619
653
|
try {
|
|
620
|
-
const spinner =
|
|
654
|
+
const spinner = ora4("Starting model tests...").info();
|
|
621
655
|
const app = express();
|
|
622
656
|
const webServer = __using(_stack, await startWebServer(app, options.port));
|
|
623
657
|
try {
|
|
@@ -638,13 +672,13 @@ async function handleAction3(options) {
|
|
|
638
672
|
ctx
|
|
639
673
|
);
|
|
640
674
|
const handleOutput = createOutputHandler(modelIOChecks, ctx);
|
|
641
|
-
const inputPath =
|
|
675
|
+
const inputPath = path11.join(ctx.projectRoot, options.inputDir);
|
|
642
676
|
app.use(express.json());
|
|
643
677
|
app.use("/data", express.static(inputPath));
|
|
644
678
|
app.get("/input/:filename", handleInput);
|
|
645
679
|
app.put("/output", handleOutput);
|
|
646
680
|
spinner.start("Running model container");
|
|
647
|
-
const webserverURL = `http://
|
|
681
|
+
const webserverURL = `http://host.docker.internal:${options.port}`;
|
|
648
682
|
await runModelContainer(dockerImage, webserverURL, ctx);
|
|
649
683
|
spinner.succeed("Model run complete");
|
|
650
684
|
for (const inputFile of inputFiles) {
|
|
@@ -659,7 +693,7 @@ async function handleAction3(options) {
|
|
|
659
693
|
spinner.fail("Model I/O test failed");
|
|
660
694
|
}
|
|
661
695
|
if (modelIOChecks.isOutputHandled()) {
|
|
662
|
-
const outputPath =
|
|
696
|
+
const outputPath = path11.join(
|
|
663
697
|
ctx.projectRoot,
|
|
664
698
|
TEST_OUTPUT_DATA_DIR,
|
|
665
699
|
TEST_OUTPUT_FILENAME
|
|
@@ -671,7 +705,7 @@ ${clickHereLink} to view output (${fileLink})
|
|
|
671
705
|
`);
|
|
672
706
|
}
|
|
673
707
|
} catch (error) {
|
|
674
|
-
if (error instanceof
|
|
708
|
+
if (error instanceof ZodError3) {
|
|
675
709
|
spinner.fail("Config validation failed");
|
|
676
710
|
logZodIssues(error);
|
|
677
711
|
} else if (error instanceof Error) {
|
|
@@ -697,123 +731,95 @@ function registerTestCommand(program2) {
|
|
|
697
731
|
"-i, --input-dir <path>",
|
|
698
732
|
"The input test data directory",
|
|
699
733
|
TEST_INPUT_DATA_DIR
|
|
700
|
-
).option("-p, --port <port>", "The port to run the webserver on", "9800").action(
|
|
734
|
+
).option("-p, --port <port>", "The port to run the webserver on", "9800").action(handleAction4);
|
|
701
735
|
}
|
|
702
736
|
|
|
703
|
-
// src/
|
|
704
|
-
import
|
|
705
|
-
import path11 from "path";
|
|
706
|
-
import { ZodError as ZodError3 } from "zod";
|
|
737
|
+
// src/program.ts
|
|
738
|
+
import { Command } from "commander";
|
|
707
739
|
|
|
708
|
-
//
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
740
|
+
// package.json
|
|
741
|
+
var package_default = {
|
|
742
|
+
name: "@pd4castr/cli",
|
|
743
|
+
version: "0.0.5",
|
|
744
|
+
description: "CLI tool for creating, testing, and publishing pd4castr models",
|
|
745
|
+
main: "dist/index.js",
|
|
746
|
+
type: "module",
|
|
747
|
+
bin: {
|
|
748
|
+
pd4castr: "dist/index.js"
|
|
749
|
+
},
|
|
750
|
+
files: [
|
|
751
|
+
"dist/**/*"
|
|
752
|
+
],
|
|
753
|
+
scripts: {
|
|
754
|
+
build: "tsup",
|
|
755
|
+
dev: "tsup --watch",
|
|
756
|
+
cli: "node dist/index.js",
|
|
757
|
+
test: "vitest run",
|
|
758
|
+
"test:watch": "vitest",
|
|
759
|
+
"test:coverage": "vitest run --coverage",
|
|
760
|
+
lint: "eslint .",
|
|
761
|
+
"lint:fix": "eslint . --fix",
|
|
762
|
+
format: "prettier --write .",
|
|
763
|
+
"format:check": "prettier --check .",
|
|
764
|
+
"type-check": "tsc --noEmit",
|
|
765
|
+
prepublishOnly: "yarn build"
|
|
766
|
+
},
|
|
767
|
+
keywords: [
|
|
768
|
+
"cli",
|
|
769
|
+
"pd4castr"
|
|
770
|
+
],
|
|
771
|
+
license: "UNLICENSED",
|
|
772
|
+
repository: {
|
|
773
|
+
type: "git",
|
|
774
|
+
url: "git+https://github.com/pipelabs/pd4castr-cli.git"
|
|
775
|
+
},
|
|
776
|
+
bugs: {
|
|
777
|
+
url: "https://github.com/pipelabs/pd4castr-cli/issues"
|
|
778
|
+
},
|
|
779
|
+
homepage: "https://github.com/pipelabs/pd4castr-cli#readme",
|
|
780
|
+
devDependencies: {
|
|
781
|
+
"@types/express": "4.17.21",
|
|
782
|
+
"@types/node": "24.1.0",
|
|
783
|
+
"@types/supertest": "6.0.3",
|
|
784
|
+
"@typescript-eslint/eslint-plugin": "8.38.0",
|
|
785
|
+
"@typescript-eslint/parser": "8.38.0",
|
|
786
|
+
eslint: "9.32.0",
|
|
787
|
+
"eslint-config-prettier": "10.1.8",
|
|
788
|
+
"eslint-plugin-simple-import-sort": "12.1.1",
|
|
789
|
+
"eslint-plugin-unicorn": "60.0.0",
|
|
790
|
+
"eslint-plugin-vitest": "0.5.4",
|
|
791
|
+
"jest-extended": "6.0.0",
|
|
792
|
+
memfs: "4.23.0",
|
|
793
|
+
msw: "2.10.4",
|
|
794
|
+
prettier: "3.6.2",
|
|
795
|
+
supertest: "7.1.4",
|
|
796
|
+
tsup: "8.5.0",
|
|
797
|
+
typescript: "5.8.3",
|
|
798
|
+
"typescript-eslint": "8.38.0",
|
|
799
|
+
vitest: "3.2.4"
|
|
800
|
+
},
|
|
801
|
+
dependencies: {
|
|
802
|
+
"@inquirer/prompts": "7.7.1",
|
|
803
|
+
auth0: "4.27.0",
|
|
804
|
+
commander: "14.0.0",
|
|
805
|
+
execa: "9.6.0",
|
|
806
|
+
express: "4.21.2",
|
|
807
|
+
ky: "1.8.2",
|
|
808
|
+
lilconfig: "3.1.3",
|
|
809
|
+
ora: "8.2.0",
|
|
810
|
+
slugify: "1.6.6",
|
|
811
|
+
tiged: "2.12.7",
|
|
812
|
+
"tiny-invariant": "1.3.3",
|
|
813
|
+
zod: "4.0.14"
|
|
814
|
+
},
|
|
815
|
+
engines: {
|
|
816
|
+
node: ">=20.0.0"
|
|
714
817
|
}
|
|
715
|
-
invariant(config.accessToken, "Access token is required");
|
|
716
|
-
invariant(config.accessTokenExpiresAt, "Access token expiry is required");
|
|
717
|
-
return {
|
|
718
|
-
accessToken: config.accessToken,
|
|
719
|
-
expiresAt: config.accessTokenExpiresAt
|
|
720
|
-
};
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
// src/commands/fetch/utils/fetch-aemo-data.ts
|
|
724
|
-
import path9 from "path";
|
|
725
|
-
import fs5 from "fs/promises";
|
|
726
|
-
|
|
727
|
-
// src/api.ts
|
|
728
|
-
import ky2 from "ky";
|
|
729
|
-
var api = ky2.create({
|
|
730
|
-
prefixUrl: process.env.PD4CASTR_API_URL ?? API_URL
|
|
731
|
-
});
|
|
732
|
-
|
|
733
|
-
// src/commands/fetch/utils/fetch-aemo-data.ts
|
|
734
|
-
async function fetchAEMOData(dataFetcher, authCtx, ctx) {
|
|
735
|
-
const queryPath = path9.resolve(
|
|
736
|
-
ctx.projectRoot,
|
|
737
|
-
dataFetcher.config.fetchQuery
|
|
738
|
-
);
|
|
739
|
-
const querySQL = await fs5.readFile(queryPath, "utf8");
|
|
740
|
-
const headers = { Authorization: `Bearer ${authCtx.accessToken}` };
|
|
741
|
-
const payload2 = { query: querySQL, type: "AEMO_MMS" };
|
|
742
|
-
const result = await api.post("data-fetcher/query", { json: payload2, headers }).json();
|
|
743
|
-
return result;
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
// src/commands/fetch/utils/get-fetcher.ts
|
|
747
|
-
var DATA_FETCHER_FNS = {
|
|
748
|
-
AEMO_MMS: fetchAEMOData
|
|
749
818
|
};
|
|
750
|
-
function getFetcher(type) {
|
|
751
|
-
const fetcher = DATA_FETCHER_FNS[type];
|
|
752
|
-
if (!fetcher) {
|
|
753
|
-
throw new Error(`Unsupported data fetcher type: ${type}`);
|
|
754
|
-
}
|
|
755
|
-
return fetcher;
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
// src/commands/fetch/utils/write-test-data.ts
|
|
759
|
-
import path10 from "path";
|
|
760
|
-
import fs6 from "fs/promises";
|
|
761
|
-
async function writeTestData(inputData, modelInput, inputDataDir, ctx) {
|
|
762
|
-
const inputDir = path10.resolve(ctx.projectRoot, inputDataDir);
|
|
763
|
-
await fs6.mkdir(inputDir, { recursive: true });
|
|
764
|
-
const inputFilename = getInputFilename(modelInput);
|
|
765
|
-
const inputPath = path10.resolve(inputDir, inputFilename);
|
|
766
|
-
await fs6.writeFile(inputPath, JSON.stringify(inputData, void 0, 2));
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
// src/commands/fetch/handle-action.ts
|
|
770
|
-
var FETCHABLE_DATA_FETCHER_TYPES = /* @__PURE__ */ new Set(["AEMO_MMS"]);
|
|
771
|
-
async function handleAction4(options) {
|
|
772
|
-
const spinner = ora4("Starting data fetch...").start();
|
|
773
|
-
try {
|
|
774
|
-
const authCtx = await getAuth();
|
|
775
|
-
const ctx = await loadProjectConfig();
|
|
776
|
-
for (const input2 of ctx.config.inputs) {
|
|
777
|
-
if (!input2.fetcher) {
|
|
778
|
-
spinner.warn(`\`${input2.key}\` - no data fetcher defined, skipping`);
|
|
779
|
-
continue;
|
|
780
|
-
}
|
|
781
|
-
if (!FETCHABLE_DATA_FETCHER_TYPES.has(input2.fetcher.type)) {
|
|
782
|
-
spinner.warn(
|
|
783
|
-
`\`${input2.key}\` (${input2.fetcher.type}) - unsupported, skipping`
|
|
784
|
-
);
|
|
785
|
-
continue;
|
|
786
|
-
}
|
|
787
|
-
spinner.start(`\`${input2.key}\` (${input2.fetcher.type}) - fetching...`);
|
|
788
|
-
const fetchData = getFetcher(input2.fetcher.type);
|
|
789
|
-
const output = await fetchData(input2.fetcher, authCtx, ctx);
|
|
790
|
-
await writeTestData(output, input2, options.inputDir, ctx);
|
|
791
|
-
spinner.succeed(`\`${input2.key}\` (${input2.fetcher.type}) - fetched`);
|
|
792
|
-
}
|
|
793
|
-
const inputPath = path11.resolve(ctx.projectRoot, options.inputDir);
|
|
794
|
-
const link = createLink("Click here", `file://${inputPath}`);
|
|
795
|
-
console.log(`
|
|
796
|
-
${link} to view fetched data
|
|
797
|
-
`);
|
|
798
|
-
} catch (error) {
|
|
799
|
-
if (error instanceof ZodError3) {
|
|
800
|
-
spinner.fail("Config validation failed");
|
|
801
|
-
logZodIssues(error);
|
|
802
|
-
} else if (error instanceof Error) {
|
|
803
|
-
spinner.fail(error.message);
|
|
804
|
-
}
|
|
805
|
-
process.exit(1);
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
819
|
|
|
809
|
-
// src/
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
"-i, --input-dir <path>",
|
|
813
|
-
"The input test data directory",
|
|
814
|
-
TEST_INPUT_DATA_DIR
|
|
815
|
-
).action(handleAction4);
|
|
816
|
-
}
|
|
820
|
+
// src/program.ts
|
|
821
|
+
var program = new Command();
|
|
822
|
+
program.name("pd4castr").description("CLI tool for pd4castr").version(package_default.version);
|
|
817
823
|
|
|
818
824
|
// src/index.ts
|
|
819
825
|
registerInitCommand(program);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pd4castr/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "CLI tool for creating, testing, and publishing pd4castr models",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"@typescript-eslint/parser": "8.38.0",
|
|
46
46
|
"eslint": "9.32.0",
|
|
47
47
|
"eslint-config-prettier": "10.1.8",
|
|
48
|
+
"eslint-plugin-simple-import-sort": "12.1.1",
|
|
48
49
|
"eslint-plugin-unicorn": "60.0.0",
|
|
49
50
|
"eslint-plugin-vitest": "0.5.4",
|
|
50
51
|
"jest-extended": "6.0.0",
|