@tiangong-ai/cli 0.0.13 → 0.0.15

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 (63) hide show
  1. package/AGENTS.md +4 -2
  2. package/README.md +59 -2
  3. package/dist/args.d.ts +13 -0
  4. package/dist/args.js +72 -0
  5. package/dist/args.js.map +1 -0
  6. package/dist/cli.d.ts +5 -21
  7. package/dist/cli.js +27 -445
  8. package/dist/cli.js.map +1 -1
  9. package/dist/data.d.ts +3 -0
  10. package/dist/data.js +16 -0
  11. package/dist/data.js.map +1 -0
  12. package/dist/edge-search.d.ts +54 -0
  13. package/dist/edge-search.js +108 -0
  14. package/dist/edge-search.js.map +1 -0
  15. package/dist/education/commands.d.ts +2 -0
  16. package/dist/education/commands.js +144 -0
  17. package/dist/education/commands.js.map +1 -0
  18. package/dist/education/config.d.ts +24 -0
  19. package/dist/education/config.js +127 -0
  20. package/dist/education/config.js.map +1 -0
  21. package/dist/education/types.d.ts +1 -0
  22. package/dist/education/types.js +2 -0
  23. package/dist/education/types.js.map +1 -0
  24. package/dist/env.d.ts +2 -0
  25. package/dist/env.js +42 -0
  26. package/dist/env.js.map +1 -0
  27. package/dist/errors.d.ts +24 -0
  28. package/dist/errors.js +53 -0
  29. package/dist/errors.js.map +1 -0
  30. package/dist/http.d.ts +28 -0
  31. package/dist/http.js +80 -0
  32. package/dist/http.js.map +1 -0
  33. package/dist/io.d.ts +11 -0
  34. package/dist/io.js +33 -0
  35. package/dist/io.js.map +1 -0
  36. package/dist/kb/client.d.ts +19 -0
  37. package/dist/kb/client.js +71 -0
  38. package/dist/kb/client.js.map +1 -0
  39. package/dist/kb/config.d.ts +12 -0
  40. package/dist/kb/config.js +26 -0
  41. package/dist/kb/config.js.map +1 -0
  42. package/dist/kb/pipeline-health.d.ts +10 -0
  43. package/dist/kb/pipeline-health.js +68 -0
  44. package/dist/kb/pipeline-health.js.map +1 -0
  45. package/dist/kb/selector.d.ts +3 -0
  46. package/dist/kb/selector.js +40 -0
  47. package/dist/kb/selector.js.map +1 -0
  48. package/dist/kb/status.d.ts +19 -0
  49. package/dist/kb/status.js +74 -0
  50. package/dist/kb/status.js.map +1 -0
  51. package/dist/research/commands.d.ts +2 -0
  52. package/dist/research/commands.js +147 -0
  53. package/dist/research/commands.js.map +1 -0
  54. package/dist/research/config.d.ts +23 -0
  55. package/dist/research/config.js +120 -0
  56. package/dist/research/config.js.map +1 -0
  57. package/dist/research/types.d.ts +1 -0
  58. package/dist/research/types.js +2 -0
  59. package/dist/research/types.js.map +1 -0
  60. package/dist/strict-args.d.ts +8 -0
  61. package/dist/strict-args.js +63 -0
  62. package/dist/strict-args.js.map +1 -0
  63. package/package.json +2 -2
@@ -0,0 +1,54 @@
1
+ import { type FetchLike } from "./http.js";
2
+ export declare const DEFAULT_EDGE_SEARCH_API_BASE_URL = "https://qyyqlnwqwgvzxnccnbgm.supabase.co/functions/v1";
3
+ export type EdgeSearchAuthStrategy = "apiKey" | "bearerOrApiKey";
4
+ export type EdgeSearchSourceSpec = {
5
+ endpoint: string;
6
+ includeRegion: boolean;
7
+ authStrategy: EdgeSearchAuthStrategy;
8
+ };
9
+ export interface EdgeSearchSourceConfig<SourceId extends string> {
10
+ source: SourceId;
11
+ url: string;
12
+ apiKey: string;
13
+ bearerToken?: string | undefined;
14
+ region: string;
15
+ authStrategy: EdgeSearchAuthStrategy;
16
+ includeRegion: boolean;
17
+ }
18
+ export interface EdgeSearchRequestPlan<SourceId extends string> {
19
+ source: SourceId;
20
+ request: {
21
+ method: "POST";
22
+ url: string;
23
+ headers: Record<string, string>;
24
+ inputPath?: string | undefined;
25
+ body: unknown;
26
+ timeoutMs: number;
27
+ };
28
+ }
29
+ export interface EdgeSearchDryRunResult<SourceId extends string> {
30
+ dryRun: true;
31
+ requests: Array<EdgeSearchRequestPlan<SourceId>>;
32
+ }
33
+ export interface EdgeSearchResponse<SourceId extends string> {
34
+ source: SourceId;
35
+ response: unknown;
36
+ }
37
+ export interface EdgeSearchRunResult<SourceId extends string> {
38
+ dryRun: false;
39
+ responses: Array<EdgeSearchResponse<SourceId>>;
40
+ }
41
+ export type EdgeSearchResult<SourceId extends string> = EdgeSearchDryRunResult<SourceId> | EdgeSearchRunResult<SourceId>;
42
+ export declare function deriveSupabaseProjectBaseUrl(apiBaseUrl: string): string;
43
+ export declare function deriveSupabaseRestBaseUrl(apiBaseUrl: string): string;
44
+ export declare const deriveSupabaseFunctionsBaseUrl: (apiBaseUrl: string) => string;
45
+ export declare function edgeFunctionUrl(apiBaseUrl: string, endpoint: string): string;
46
+ export declare function runEdgeSearch<SourceId extends string>(input: {
47
+ body: unknown;
48
+ inputPath?: string | undefined;
49
+ sources: Array<EdgeSearchSourceConfig<SourceId>>;
50
+ timeoutMs: number;
51
+ dryRun: boolean;
52
+ missingCredentialHelp: string;
53
+ fetchImpl?: FetchLike | undefined;
54
+ }): Promise<EdgeSearchResult<SourceId>>;
@@ -0,0 +1,108 @@
1
+ import { CliError } from "./errors.js";
2
+ import { postJson } from "./http.js";
3
+ export const DEFAULT_EDGE_SEARCH_API_BASE_URL = "https://qyyqlnwqwgvzxnccnbgm.supabase.co/functions/v1";
4
+ function normalizeBaseUrl(apiBaseUrl) {
5
+ const normalized = apiBaseUrl.trim().replace(/\/+$/u, "");
6
+ if (!normalized) {
7
+ throw new CliError("Cannot derive Supabase URLs from an empty API base URL.", {
8
+ code: "EDGE_SEARCH_API_BASE_URL_INVALID",
9
+ exitCode: 2,
10
+ });
11
+ }
12
+ return normalized;
13
+ }
14
+ export function deriveSupabaseProjectBaseUrl(apiBaseUrl) {
15
+ const normalized = normalizeBaseUrl(apiBaseUrl);
16
+ if (normalized.endsWith("/functions/v1")) {
17
+ return normalized.replace(/\/functions\/v1$/u, "");
18
+ }
19
+ if (normalized.endsWith("/rest/v1")) {
20
+ return normalized.replace(/\/rest\/v1$/u, "");
21
+ }
22
+ if (/^https?:\/\/[^/]+$/u.test(normalized)) {
23
+ return normalized;
24
+ }
25
+ throw new CliError("Cannot derive a Supabase project base URL from TIANGONG_AI_API_BASE_URL. Use a Supabase project base URL, a /functions/v1 base URL, or a /rest/v1 base URL.", {
26
+ code: "EDGE_SEARCH_API_BASE_URL_INVALID",
27
+ exitCode: 2,
28
+ details: normalized,
29
+ });
30
+ }
31
+ export function deriveSupabaseRestBaseUrl(apiBaseUrl) {
32
+ return `${deriveSupabaseProjectBaseUrl(apiBaseUrl)}/rest/v1`;
33
+ }
34
+ export const deriveSupabaseFunctionsBaseUrl = (apiBaseUrl) => `${deriveSupabaseProjectBaseUrl(apiBaseUrl)}/functions/v1`;
35
+ export function edgeFunctionUrl(apiBaseUrl, endpoint) {
36
+ return `${deriveSupabaseFunctionsBaseUrl(apiBaseUrl)}/${endpoint.replace(/^\/+/u, "")}`;
37
+ }
38
+ export async function runEdgeSearch(input) {
39
+ const plans = input.sources.map((source) => edgeSearchRequestPlan({
40
+ source,
41
+ body: input.body,
42
+ inputPath: input.inputPath,
43
+ timeoutMs: input.timeoutMs,
44
+ masked: input.dryRun,
45
+ missingCredentialHelp: input.missingCredentialHelp,
46
+ }));
47
+ if (input.dryRun) {
48
+ return { dryRun: true, requests: plans };
49
+ }
50
+ const responses = [];
51
+ for (const plan of plans) {
52
+ responses.push({
53
+ source: plan.source,
54
+ response: await postJson({
55
+ url: plan.request.url,
56
+ headers: plan.request.headers,
57
+ body: plan.request.body,
58
+ timeoutMs: input.timeoutMs,
59
+ fetchImpl: input.fetchImpl ?? fetch,
60
+ }),
61
+ });
62
+ }
63
+ return { dryRun: false, responses };
64
+ }
65
+ function edgeSearchRequestPlan(input) {
66
+ const headers = edgeSearchHeaders(input.source, input.masked, input.missingCredentialHelp);
67
+ return {
68
+ source: input.source.source,
69
+ request: {
70
+ method: "POST",
71
+ url: input.source.url,
72
+ headers,
73
+ ...(input.inputPath ? { inputPath: input.inputPath } : {}),
74
+ body: input.body,
75
+ timeoutMs: input.timeoutMs,
76
+ },
77
+ };
78
+ }
79
+ function edgeSearchHeaders(source, masked, missingCredentialHelp) {
80
+ if (source.authStrategy === "apiKey" && !source.apiKey) {
81
+ throw new CliError(`Missing ${source.source} search credentials. ${missingCredentialHelp}`, {
82
+ code: "EDGE_SEARCH_CREDENTIALS_REQUIRED",
83
+ exitCode: 2,
84
+ details: { source: source.source },
85
+ });
86
+ }
87
+ if (source.authStrategy === "bearerOrApiKey" && !source.apiKey && !source.bearerToken) {
88
+ throw new CliError(`Missing ${source.source} search credentials. ${missingCredentialHelp}`, {
89
+ code: "EDGE_SEARCH_CREDENTIALS_REQUIRED",
90
+ exitCode: 2,
91
+ details: { source: source.source },
92
+ });
93
+ }
94
+ const headers = {
95
+ "Content-Type": "application/json",
96
+ };
97
+ if (source.includeRegion && source.region) {
98
+ headers["x-region"] = source.region;
99
+ }
100
+ if (source.authStrategy === "bearerOrApiKey" && source.bearerToken) {
101
+ headers.Authorization = `Bearer ${masked ? "****" : source.bearerToken}`;
102
+ }
103
+ else {
104
+ headers["x-api-key"] = masked ? "****" : source.apiKey;
105
+ }
106
+ return headers;
107
+ }
108
+ //# sourceMappingURL=edge-search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edge-search.js","sourceRoot":"","sources":["../src/edge-search.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { CliIO } from "../io.js";
2
+ export declare function runEducationCommand(argv: string[], io: CliIO): Promise<number>;
@@ -0,0 +1,144 @@
1
+ import { runEdgeSearch } from "../edge-search.js";
2
+ import { CliError } from "../errors.js";
3
+ import { readJsonInput, stringifyJson, write } from "../io.js";
4
+ import { parseStrictArgs, strictBoolean, strictString } from "../strict-args.js";
5
+ import { educationSourceConfig, parseEducationSources, resolveEducationConfig } from "./config.js";
6
+ const EDUCATION_SEARCH_OPTIONS = {
7
+ help: "boolean",
8
+ json: "boolean",
9
+ "dry-run": "boolean",
10
+ input: "string",
11
+ query: "string",
12
+ sources: "string",
13
+ "api-key": "string",
14
+ "api-base-url": "string",
15
+ "bearer-token": "string",
16
+ "course-api-key": "string",
17
+ "edu-api-key": "string",
18
+ "textbook-api-key": "string",
19
+ "course-url": "string",
20
+ "edu-url": "string",
21
+ "textbook-url": "string",
22
+ region: "string",
23
+ timeout: "string",
24
+ "top-k": "string",
25
+ "ext-k": "string",
26
+ };
27
+ export async function runEducationCommand(argv, io) {
28
+ const [subcommand, ...rest] = argv;
29
+ if (!subcommand || subcommand === "--help" || subcommand === "-h") {
30
+ write(io.stdout, educationHelp());
31
+ return 0;
32
+ }
33
+ if (subcommand === "search") {
34
+ return runEducationSearchCommand(rest, io);
35
+ }
36
+ write(io.stdout, educationHelp());
37
+ return 1;
38
+ }
39
+ function educationHelp() {
40
+ return `Tiangong education commands
41
+
42
+ Usage:
43
+ tiangong-ai education search --input <request.json> [--sources default|all|course|edu|textbook] [--dry-run] [--json]
44
+ tiangong-ai education search --query <query> [--sources default|all|course|edu|textbook] [--dry-run] [--json]
45
+
46
+ Education search options:
47
+ --input <file>
48
+ JSON request body to forward unchanged to each selected edge function.
49
+ --query <text>
50
+ Convenience mode; builds {"query": text} plus optional --top-k/--ext-k.
51
+ --sources <csv> (default course; all = course,edu,textbook)
52
+ --dry-run
53
+ Print the exact POST request plan with masked credentials.
54
+ --api-key <token> or TIANGONG_AI_APIKEY
55
+ --api-base-url <url>
56
+ Supabase project root, /functions/v1, or /rest/v1 base URL.
57
+ --bearer-token <token> for scoped course search
58
+ --course-api-key <token>, --edu-api-key <token>, --textbook-api-key <token>
59
+ --course-url <url>, --edu-url <url>, --textbook-url <url>
60
+ --region <name>
61
+ --timeout <seconds>
62
+ --top-k <n>
63
+ --ext-k <n>
64
+ --json
65
+ `;
66
+ }
67
+ async function runEducationSearchCommand(argv, io) {
68
+ const args = parseStrictArgs(argv, EDUCATION_SEARCH_OPTIONS, "education search");
69
+ if (strictBoolean(args, "help")) {
70
+ write(io.stdout, educationHelp());
71
+ return 0;
72
+ }
73
+ if (args.positionals.length > 0) {
74
+ throw new CliError("education search does not accept positional arguments.", {
75
+ code: "INVALID_ARGS",
76
+ exitCode: 2,
77
+ details: { positionals: args.positionals },
78
+ });
79
+ }
80
+ const sourceIds = parseEducationSources(strictString(args, "sources"));
81
+ const config = resolveEducationConfig(args, io.env);
82
+ const inputPath = strictString(args, "input");
83
+ const body = educationRequestBody(args);
84
+ const result = await runEdgeSearch({
85
+ body,
86
+ inputPath,
87
+ sources: sourceIds.map((source) => ({ source, ...educationSourceConfig(config, source) })),
88
+ timeoutMs: config.timeoutSeconds * 1000,
89
+ dryRun: strictBoolean(args, "dry-run"),
90
+ missingCredentialHelp: "Provide --api-key, --bearer-token for course search, a source-specific API key option, or set TIANGONG_AI_APIKEY.",
91
+ });
92
+ writeSearchResult(io, result, strictBoolean(args, "json"));
93
+ return 0;
94
+ }
95
+ function educationRequestBody(args) {
96
+ const inputPath = strictString(args, "input");
97
+ if (inputPath) {
98
+ rejectInputModeBodyFlags(args, ["query", "top-k", "ext-k"]);
99
+ return readJsonInput(inputPath);
100
+ }
101
+ const query = strictString(args, "query");
102
+ if (!query) {
103
+ throw new CliError("Usage: tiangong-ai education search --input <file> or --query <query>", {
104
+ code: "EDUCATION_QUERY_REQUIRED",
105
+ exitCode: 2,
106
+ });
107
+ }
108
+ const body = { query };
109
+ const topK = optionalPositiveInteger(strictString(args, "top-k"), "--top-k");
110
+ const extK = optionalPositiveInteger(strictString(args, "ext-k"), "--ext-k");
111
+ if (topK !== undefined)
112
+ body.topK = topK;
113
+ if (extK !== undefined)
114
+ body.extK = extK;
115
+ return body;
116
+ }
117
+ function rejectInputModeBodyFlags(args, bodyFlagNames) {
118
+ const present = bodyFlagNames.filter((name) => args.flags.has(name));
119
+ if (present.length === 0)
120
+ return;
121
+ throw new CliError(`--input cannot be combined with body-building flags: ${present.join(", ")}`, {
122
+ code: "INVALID_ARGS",
123
+ exitCode: 2,
124
+ details: { flags: present },
125
+ });
126
+ }
127
+ function optionalPositiveInteger(value, label) {
128
+ if (!value)
129
+ return undefined;
130
+ const parsed = Number(value);
131
+ if (!Number.isInteger(parsed) || parsed <= 0) {
132
+ throw new CliError(`${label} must be a positive integer.`, {
133
+ code: "INVALID_NUMERIC_OPTION",
134
+ exitCode: 2,
135
+ details: { label, value },
136
+ });
137
+ }
138
+ return parsed;
139
+ }
140
+ function writeSearchResult(io, result, compact) {
141
+ const output = !result.dryRun && result.responses.length === 1 ? result.responses[0]?.response : result;
142
+ write(io.stdout, stringifyJson(output, compact));
143
+ }
144
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/education/commands.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { type EdgeSearchAuthStrategy, type EdgeSearchSourceSpec } from "../edge-search.js";
2
+ import { type StrictParsedArgs } from "../strict-args.js";
3
+ import type { EducationSourceId } from "./types.js";
4
+ export declare const DEFAULT_COURSE_SEARCH_ENDPOINT = "course_search";
5
+ export declare const DEFAULT_EDU_SEARCH_ENDPOINT = "edu_search";
6
+ export declare const DEFAULT_TEXTBOOK_SEARCH_ENDPOINT = "textbook_search";
7
+ export declare const DEFAULT_EDUCATION_SOURCES: EducationSourceId[];
8
+ export declare const ALL_EDUCATION_SOURCES: EducationSourceId[];
9
+ export declare const EDUCATION_SEARCH_SOURCES: Record<EducationSourceId, EdgeSearchSourceSpec>;
10
+ export interface EducationConfig {
11
+ timeoutSeconds: number;
12
+ sources: Record<EducationSourceId, EducationSourceConfig>;
13
+ }
14
+ export interface EducationSourceConfig {
15
+ url: string;
16
+ apiKey: string;
17
+ bearerToken: string | undefined;
18
+ region: string;
19
+ authStrategy: EdgeSearchAuthStrategy;
20
+ includeRegion: boolean;
21
+ }
22
+ export declare function resolveEducationConfig(args: StrictParsedArgs, env: NodeJS.ProcessEnv): EducationConfig;
23
+ export declare function educationSourceConfig(config: EducationConfig, source: EducationSourceId): EducationSourceConfig;
24
+ export declare function parseEducationSources(value: string | undefined): EducationSourceId[];
@@ -0,0 +1,127 @@
1
+ import { firstEnv } from "../env.js";
2
+ import { DEFAULT_EDGE_SEARCH_API_BASE_URL, edgeFunctionUrl, } from "../edge-search.js";
3
+ import { CliError } from "../errors.js";
4
+ import { strictString } from "../strict-args.js";
5
+ export const DEFAULT_COURSE_SEARCH_ENDPOINT = "course_search";
6
+ export const DEFAULT_EDU_SEARCH_ENDPOINT = "edu_search";
7
+ export const DEFAULT_TEXTBOOK_SEARCH_ENDPOINT = "textbook_search";
8
+ export const DEFAULT_EDUCATION_SOURCES = ["course"];
9
+ export const ALL_EDUCATION_SOURCES = ["course", "edu", "textbook"];
10
+ export const EDUCATION_SEARCH_SOURCES = {
11
+ course: {
12
+ endpoint: DEFAULT_COURSE_SEARCH_ENDPOINT,
13
+ includeRegion: true,
14
+ authStrategy: "bearerOrApiKey",
15
+ },
16
+ edu: {
17
+ endpoint: DEFAULT_EDU_SEARCH_ENDPOINT,
18
+ includeRegion: true,
19
+ authStrategy: "apiKey",
20
+ },
21
+ textbook: {
22
+ endpoint: DEFAULT_TEXTBOOK_SEARCH_ENDPOINT,
23
+ includeRegion: true,
24
+ authStrategy: "apiKey",
25
+ },
26
+ };
27
+ export function resolveEducationConfig(args, env) {
28
+ const timeoutSeconds = getPositiveIntegerValue(strictString(args, "timeout") ?? firstEnv(env, "TIANGONG_EDUCATION_TIMEOUT"), 120, "--timeout");
29
+ const apiKey = strictString(args, "api-key") ?? firstEnv(env, "TIANGONG_AI_APIKEY") ?? "";
30
+ const bearerToken = strictString(args, "bearer-token") ??
31
+ firstEnv(env, "TIANGONG_EDUCATION_BEARER_TOKEN", "TIANGONG_KB_READ_TOKEN");
32
+ const apiBaseUrl = strictString(args, "api-base-url") ??
33
+ firstEnv(env, "TIANGONG_EDUCATION_API_BASE_URL", "TIANGONG_AI_SEARCH_API_BASE_URL", "TIANGONG_AI_API_BASE_URL") ??
34
+ DEFAULT_EDGE_SEARCH_API_BASE_URL;
35
+ const region = strictString(args, "region") ?? firstEnv(env, "TIANGONG_REGION") ?? "us-east-1";
36
+ return {
37
+ timeoutSeconds,
38
+ sources: {
39
+ course: {
40
+ url: strictString(args, "course-url") ??
41
+ firstEnv(env, "TIANGONG_COURSE_SEARCH_URL") ??
42
+ edgeFunctionUrl(apiBaseUrl, EDUCATION_SEARCH_SOURCES.course.endpoint),
43
+ apiKey: strictString(args, "course-api-key") ?? firstEnv(env, "TIANGONG_COURSE_APIKEY") ?? apiKey,
44
+ bearerToken,
45
+ region,
46
+ authStrategy: EDUCATION_SEARCH_SOURCES.course.authStrategy,
47
+ includeRegion: EDUCATION_SEARCH_SOURCES.course.includeRegion,
48
+ },
49
+ edu: {
50
+ url: strictString(args, "edu-url") ??
51
+ firstEnv(env, "TIANGONG_EDU_SEARCH_URL") ??
52
+ edgeFunctionUrl(apiBaseUrl, EDUCATION_SEARCH_SOURCES.edu.endpoint),
53
+ apiKey: strictString(args, "edu-api-key") ?? firstEnv(env, "TIANGONG_EDU_APIKEY") ?? apiKey,
54
+ bearerToken: undefined,
55
+ region,
56
+ authStrategy: EDUCATION_SEARCH_SOURCES.edu.authStrategy,
57
+ includeRegion: EDUCATION_SEARCH_SOURCES.edu.includeRegion,
58
+ },
59
+ textbook: {
60
+ url: strictString(args, "textbook-url") ??
61
+ firstEnv(env, "TIANGONG_TEXTBOOK_SEARCH_URL") ??
62
+ edgeFunctionUrl(apiBaseUrl, EDUCATION_SEARCH_SOURCES.textbook.endpoint),
63
+ apiKey: strictString(args, "textbook-api-key") ??
64
+ firstEnv(env, "TIANGONG_TEXTBOOK_APIKEY") ??
65
+ apiKey,
66
+ bearerToken: undefined,
67
+ region,
68
+ authStrategy: EDUCATION_SEARCH_SOURCES.textbook.authStrategy,
69
+ includeRegion: EDUCATION_SEARCH_SOURCES.textbook.includeRegion,
70
+ },
71
+ },
72
+ };
73
+ }
74
+ export function educationSourceConfig(config, source) {
75
+ return config.sources[source];
76
+ }
77
+ export function parseEducationSources(value) {
78
+ const rawSources = (value ?? "default")
79
+ .split(",")
80
+ .map((source) => source.trim())
81
+ .filter(Boolean);
82
+ const sources = rawSources.length ? rawSources : ["default"];
83
+ return uniqueSources(sources.flatMap((source) => {
84
+ if (source === "default")
85
+ return DEFAULT_EDUCATION_SOURCES;
86
+ if (source === "all")
87
+ return ALL_EDUCATION_SOURCES;
88
+ return [parseEducationSource(source)];
89
+ }));
90
+ }
91
+ function parseEducationSource(source) {
92
+ if (source === "course")
93
+ return source;
94
+ if (source === "edu")
95
+ return source;
96
+ if (source === "textbook")
97
+ return source;
98
+ throw new CliError(`Unsupported education source: ${source}`, {
99
+ code: "EDUCATION_SOURCE_UNSUPPORTED",
100
+ exitCode: 2,
101
+ details: { source },
102
+ });
103
+ }
104
+ function uniqueSources(sources) {
105
+ const seen = new Set();
106
+ const unique = [];
107
+ for (const source of sources) {
108
+ if (seen.has(source))
109
+ continue;
110
+ seen.add(source);
111
+ unique.push(source);
112
+ }
113
+ return unique;
114
+ }
115
+ function getPositiveIntegerValue(value, defaultValue, label) {
116
+ if (!value)
117
+ return defaultValue;
118
+ const parsed = Number(value);
119
+ if (!Number.isInteger(parsed) || parsed <= 0)
120
+ throw new CliError(`${label} must be a positive integer.`, {
121
+ code: "INVALID_NUMERIC_OPTION",
122
+ exitCode: 2,
123
+ details: { label, value },
124
+ });
125
+ return parsed;
126
+ }
127
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/education/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export type EducationSourceId = "course" | "edu" | "textbook";
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/education/types.ts"],"names":[],"mappings":""}
package/dist/env.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export declare function loadDotenv(env: NodeJS.ProcessEnv): void;
2
+ export declare function firstEnv(env: NodeJS.ProcessEnv, ...names: string[]): string | undefined;
package/dist/env.js ADDED
@@ -0,0 +1,42 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { dirname, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ export function loadDotenv(env) {
5
+ const cwd = dirname(fileURLToPath(import.meta.url));
6
+ const candidates = [resolve(process.cwd(), ".env"), resolve(cwd, "../.env")];
7
+ for (const candidate of candidates) {
8
+ const content = readEnvFile(candidate);
9
+ if (!content)
10
+ continue;
11
+ for (const line of content.split(/\r?\n/)) {
12
+ const trimmed = line.trim();
13
+ if (!trimmed || trimmed.startsWith("#") || !trimmed.includes("="))
14
+ continue;
15
+ const [rawKey, ...rawValue] = trimmed.replace(/^export\s+/, "").split("=");
16
+ const key = rawKey?.trim();
17
+ if (!key || env[key])
18
+ continue;
19
+ env[key] = rawValue
20
+ .join("=")
21
+ .trim()
22
+ .replace(/^["']|["']$/g, "");
23
+ }
24
+ }
25
+ }
26
+ export function firstEnv(env, ...names) {
27
+ for (const name of names) {
28
+ const value = env[name]?.trim();
29
+ if (value)
30
+ return value;
31
+ }
32
+ return undefined;
33
+ }
34
+ function readEnvFile(path) {
35
+ try {
36
+ return readFileSync(path, "utf8");
37
+ }
38
+ catch {
39
+ return undefined;
40
+ }
41
+ }
42
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,UAAU,UAAU,CAAC,GAAsB;IAC/C,MAAM,GAAG,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACpD,MAAM,UAAU,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC;IAC7E,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,MAAM,OAAO,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;QACvC,IAAI,CAAC,OAAO;YAAE,SAAS;QACvB,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAC5B,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;gBAAE,SAAS;YAC5E,MAAM,CAAC,MAAM,EAAE,GAAG,QAAQ,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC3E,MAAM,GAAG,GAAG,MAAM,EAAE,IAAI,EAAE,CAAC;YAC3B,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC;gBAAE,SAAS;YAC/B,GAAG,CAAC,GAAG,CAAC,GAAG,QAAQ;iBAChB,IAAI,CAAC,GAAG,CAAC;iBACT,IAAI,EAAE;iBACN,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,GAAsB,EAAE,GAAG,KAAe;IACjE,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC;QAChC,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;IAC1B,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,WAAW,CAAC,IAAY;IAC/B,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC"}
@@ -0,0 +1,24 @@
1
+ export declare class CliError extends Error {
2
+ readonly code: string;
3
+ readonly details: unknown;
4
+ readonly exitCode: number;
5
+ constructor(message: string, optionsOrExitCode?: {
6
+ code?: string;
7
+ details?: unknown;
8
+ exitCode?: number;
9
+ } | number);
10
+ }
11
+ export declare class HttpError extends CliError {
12
+ readonly status: number | undefined;
13
+ readonly retryAfterSeconds: number | undefined;
14
+ readonly retryable: boolean;
15
+ constructor(message: string, status: number | undefined, retryAfterSeconds: number | undefined, retryable: boolean);
16
+ }
17
+ export type ErrorPayload = {
18
+ error: {
19
+ code: string;
20
+ message: string;
21
+ details?: unknown;
22
+ };
23
+ };
24
+ export declare function toErrorPayload(error: unknown): ErrorPayload;
package/dist/errors.js ADDED
@@ -0,0 +1,53 @@
1
+ export class CliError extends Error {
2
+ code;
3
+ details;
4
+ exitCode;
5
+ constructor(message, optionsOrExitCode = {}) {
6
+ super(message);
7
+ this.name = "CliError";
8
+ const options = typeof optionsOrExitCode === "number" ? { exitCode: optionsOrExitCode } : optionsOrExitCode;
9
+ this.code = options.code ?? "CLI_ERROR";
10
+ this.details = options.details;
11
+ this.exitCode = options.exitCode ?? 1;
12
+ }
13
+ }
14
+ export class HttpError extends CliError {
15
+ status;
16
+ retryAfterSeconds;
17
+ retryable;
18
+ constructor(message, status, retryAfterSeconds, retryable) {
19
+ super(message, {
20
+ code: "HTTP_ERROR",
21
+ details: { status, retryAfterSeconds, retryable },
22
+ });
23
+ this.status = status;
24
+ this.retryAfterSeconds = retryAfterSeconds;
25
+ this.retryable = retryable;
26
+ }
27
+ }
28
+ export function toErrorPayload(error) {
29
+ if (error instanceof CliError) {
30
+ return {
31
+ error: {
32
+ code: error.code,
33
+ message: error.message,
34
+ ...(error.details === undefined ? {} : { details: error.details }),
35
+ },
36
+ };
37
+ }
38
+ if (error instanceof Error) {
39
+ return {
40
+ error: {
41
+ code: "UNEXPECTED_ERROR",
42
+ message: error.message,
43
+ },
44
+ };
45
+ }
46
+ return {
47
+ error: {
48
+ code: "UNKNOWN_THROWN_VALUE",
49
+ message: String(error),
50
+ },
51
+ };
52
+ }
53
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,QAAS,SAAQ,KAAK;IACxB,IAAI,CAAS;IACb,OAAO,CAAU;IACjB,QAAQ,CAAS;IAE1B,YACE,OAAe,EACf,oBAAsF,EAAE;QAExF,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,MAAM,OAAO,GACX,OAAO,iBAAiB,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,iBAAiB,EAAE,CAAC,CAAC,CAAC,iBAAiB,CAAC;QAC9F,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,WAAW,CAAC;QACxC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC/B,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,CAAC,CAAC;IACxC,CAAC;CACF;AAED,MAAM,OAAO,SAAU,SAAQ,QAAQ;IAG1B;IACA;IACA;IAJX,YACE,OAAe,EACN,MAA0B,EAC1B,iBAAqC,EACrC,SAAkB;QAE3B,KAAK,CAAC,OAAO,EAAE;YACb,IAAI,EAAE,YAAY;YAClB,OAAO,EAAE,EAAE,MAAM,EAAE,iBAAiB,EAAE,SAAS,EAAE;SAClD,CAAC,CAAC;QAPM,WAAM,GAAN,MAAM,CAAoB;QAC1B,sBAAiB,GAAjB,iBAAiB,CAAoB;QACrC,cAAS,GAAT,SAAS,CAAS;IAM7B,CAAC;CACF;AAUD,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;QAC9B,OAAO;YACL,KAAK,EAAE;gBACL,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,GAAG,CAAC,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;aACnE;SACF,CAAC;IACJ,CAAC;IAED,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO;YACL,KAAK,EAAE;gBACL,IAAI,EAAE,kBAAkB;gBACxB,OAAO,EAAE,KAAK,CAAC,OAAO;aACvB;SACF,CAAC;IACJ,CAAC;IAED,OAAO;QACL,KAAK,EAAE;YACL,IAAI,EAAE,sBAAsB;YAC5B,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC;SACvB;KACF,CAAC;AACJ,CAAC"}