@qoren/cli 0.1.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.
- package/LICENSE +202 -0
- package/README.md +162 -0
- package/dist/args.d.ts +5 -0
- package/dist/args.d.ts.map +1 -0
- package/dist/args.js +23 -0
- package/dist/args.js.map +1 -0
- package/dist/commands/account.d.ts +4 -0
- package/dist/commands/account.d.ts.map +1 -0
- package/dist/commands/account.js +154 -0
- package/dist/commands/account.js.map +1 -0
- package/dist/commands/agent.d.ts +27 -0
- package/dist/commands/agent.d.ts.map +1 -0
- package/dist/commands/agent.js +968 -0
- package/dist/commands/agent.js.map +1 -0
- package/dist/commands/api.d.ts +4 -0
- package/dist/commands/api.d.ts.map +1 -0
- package/dist/commands/api.js +66 -0
- package/dist/commands/api.js.map +1 -0
- package/dist/commands/approvals.d.ts +11 -0
- package/dist/commands/approvals.d.ts.map +1 -0
- package/dist/commands/approvals.js +140 -0
- package/dist/commands/approvals.js.map +1 -0
- package/dist/commands/auth.d.ts +4 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +215 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/clients.d.ts +62 -0
- package/dist/commands/clients.d.ts.map +1 -0
- package/dist/commands/clients.js +311 -0
- package/dist/commands/clients.js.map +1 -0
- package/dist/commands/console.d.ts +4 -0
- package/dist/commands/console.d.ts.map +1 -0
- package/dist/commands/console.js +26 -0
- package/dist/commands/console.js.map +1 -0
- package/dist/commands/customAgent.d.ts +42 -0
- package/dist/commands/customAgent.d.ts.map +1 -0
- package/dist/commands/customAgent.js +449 -0
- package/dist/commands/customAgent.js.map +1 -0
- package/dist/commands/designer.d.ts +32 -0
- package/dist/commands/designer.d.ts.map +1 -0
- package/dist/commands/designer.js +682 -0
- package/dist/commands/designer.js.map +1 -0
- package/dist/commands/env.d.ts +15 -0
- package/dist/commands/env.d.ts.map +1 -0
- package/dist/commands/env.js +245 -0
- package/dist/commands/env.js.map +1 -0
- package/dist/commands/interactive.d.ts +4 -0
- package/dist/commands/interactive.d.ts.map +1 -0
- package/dist/commands/interactive.js +30 -0
- package/dist/commands/interactive.js.map +1 -0
- package/dist/commands/jobs.d.ts +4 -0
- package/dist/commands/jobs.d.ts.map +1 -0
- package/dist/commands/jobs.js +59 -0
- package/dist/commands/jobs.js.map +1 -0
- package/dist/commands/secrets.d.ts +15 -0
- package/dist/commands/secrets.d.ts.map +1 -0
- package/dist/commands/secrets.js +164 -0
- package/dist/commands/secrets.js.map +1 -0
- package/dist/commands/webhook.d.ts +4 -0
- package/dist/commands/webhook.d.ts.map +1 -0
- package/dist/commands/webhook.js +275 -0
- package/dist/commands/webhook.js.map +1 -0
- package/dist/config.d.ts +38 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +117 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +52 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +163 -0
- package/dist/context.js.map +1 -0
- package/dist/duration.d.ts +9 -0
- package/dist/duration.d.ts.map +1 -0
- package/dist/duration.js +30 -0
- package/dist/duration.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/issues.d.ts +10 -0
- package/dist/issues.d.ts.map +1 -0
- package/dist/issues.js +45 -0
- package/dist/issues.js.map +1 -0
- package/dist/jobProgress.d.ts +12 -0
- package/dist/jobProgress.d.ts.map +1 -0
- package/dist/jobProgress.js +56 -0
- package/dist/jobProgress.js.map +1 -0
- package/dist/loopback.d.ts +28 -0
- package/dist/loopback.d.ts.map +1 -0
- package/dist/loopback.js +110 -0
- package/dist/loopback.js.map +1 -0
- package/dist/migrate/apply.d.ts +29 -0
- package/dist/migrate/apply.d.ts.map +1 -0
- package/dist/migrate/apply.js +135 -0
- package/dist/migrate/apply.js.map +1 -0
- package/dist/migrate/bundle.d.ts +89 -0
- package/dist/migrate/bundle.d.ts.map +1 -0
- package/dist/migrate/bundle.js +681 -0
- package/dist/migrate/bundle.js.map +1 -0
- package/dist/migrate/tar.d.ts +9 -0
- package/dist/migrate/tar.d.ts.map +1 -0
- package/dist/migrate/tar.js +89 -0
- package/dist/migrate/tar.js.map +1 -0
- package/dist/output.d.ts +46 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +152 -0
- package/dist/output.js.map +1 -0
- package/dist/program.d.ts +17 -0
- package/dist/program.d.ts.map +1 -0
- package/dist/program.js +89 -0
- package/dist/program.js.map +1 -0
- package/dist/runtimes.d.ts +11 -0
- package/dist/runtimes.d.ts.map +1 -0
- package/dist/runtimes.js +20 -0
- package/dist/runtimes.js.map +1 -0
- package/dist/slug.d.ts +3 -0
- package/dist/slug.d.ts.map +1 -0
- package/dist/slug.js +15 -0
- package/dist/slug.js.map +1 -0
- package/dist/taskChain.d.ts +20 -0
- package/dist/taskChain.d.ts.map +1 -0
- package/dist/taskChain.js +40 -0
- package/dist/taskChain.js.map +1 -0
- package/dist/taskDelivery.d.ts +33 -0
- package/dist/taskDelivery.d.ts.map +1 -0
- package/dist/taskDelivery.js +67 -0
- package/dist/taskDelivery.js.map +1 -0
- package/dist/tui/app.d.ts +5 -0
- package/dist/tui/app.d.ts.map +1 -0
- package/dist/tui/app.js +130 -0
- package/dist/tui/app.js.map +1 -0
- package/dist/tui/index.d.ts +12 -0
- package/dist/tui/index.d.ts.map +1 -0
- package/dist/tui/index.js +34 -0
- package/dist/tui/index.js.map +1 -0
- package/dist/tui/model.d.ts +110 -0
- package/dist/tui/model.d.ts.map +1 -0
- package/dist/tui/model.js +257 -0
- package/dist/tui/model.js.map +1 -0
- package/dist/tui/overlay.d.ts +66 -0
- package/dist/tui/overlay.d.ts.map +1 -0
- package/dist/tui/overlay.js +216 -0
- package/dist/tui/overlay.js.map +1 -0
- package/dist/tui/panes/account.d.ts +5 -0
- package/dist/tui/panes/account.d.ts.map +1 -0
- package/dist/tui/panes/account.js +108 -0
- package/dist/tui/panes/account.js.map +1 -0
- package/dist/tui/panes/agents.d.ts +9 -0
- package/dist/tui/panes/agents.d.ts.map +1 -0
- package/dist/tui/panes/agents.js +340 -0
- package/dist/tui/panes/agents.js.map +1 -0
- package/dist/tui/panes/chat.d.ts +14 -0
- package/dist/tui/panes/chat.d.ts.map +1 -0
- package/dist/tui/panes/chat.js +169 -0
- package/dist/tui/panes/chat.js.map +1 -0
- package/dist/tui/panes/environments.d.ts +6 -0
- package/dist/tui/panes/environments.d.ts.map +1 -0
- package/dist/tui/panes/environments.js +364 -0
- package/dist/tui/panes/environments.js.map +1 -0
- package/dist/tui/panes/jobs.d.ts +5 -0
- package/dist/tui/panes/jobs.d.ts.map +1 -0
- package/dist/tui/panes/jobs.js +104 -0
- package/dist/tui/panes/jobs.js.map +1 -0
- package/dist/tui/store.d.ts +40 -0
- package/dist/tui/store.d.ts.map +1 -0
- package/dist/tui/store.js +124 -0
- package/dist/tui/store.js.map +1 -0
- package/dist/tui/ui.d.ts +95 -0
- package/dist/tui/ui.d.ts.map +1 -0
- package/dist/tui/ui.js +144 -0
- package/dist/tui/ui.js.map +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Qoren, QorenError } from "@qoren/sdk";
|
|
2
|
+
import { type Profile } from "./config.js";
|
|
3
|
+
export declare const VERSION = "0.1.0";
|
|
4
|
+
export type GlobalOptions = {
|
|
5
|
+
json?: boolean;
|
|
6
|
+
profile?: string;
|
|
7
|
+
apiUrl?: string;
|
|
8
|
+
color?: boolean;
|
|
9
|
+
};
|
|
10
|
+
export type Context = {
|
|
11
|
+
qoren: Qoren;
|
|
12
|
+
profile: Profile;
|
|
13
|
+
profileName: string;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* The org's plan does not include API access (a 403 with `code:
|
|
17
|
+
* "api_access_required"`). The token is fine, so this is a plan problem (exit
|
|
18
|
+
* 4), not a credential one (exit 3): logging in again would fail identically.
|
|
19
|
+
*
|
|
20
|
+
* Read off the code rather than the SDK's `isApiAccessRequired`, so a CLI
|
|
21
|
+
* built against an older SDK still tells the two apart.
|
|
22
|
+
*/
|
|
23
|
+
export declare function isApiAccessRequired(err: unknown): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* The caller's organization is being deleted (a 403 with `code:
|
|
26
|
+
* "org_deleting"`). Logging in again cannot help, so the server's message,
|
|
27
|
+
* which says what is happening, is shown instead of "credentials rejected".
|
|
28
|
+
*/
|
|
29
|
+
export declare function isOrgDeleting(err: unknown): boolean;
|
|
30
|
+
/** Build the client for this invocation, or explain how to get one. */
|
|
31
|
+
export declare function requireContext(options: GlobalOptions): Context;
|
|
32
|
+
/**
|
|
33
|
+
* The org's fleet tenant slug, which creating an environment needs.
|
|
34
|
+
*
|
|
35
|
+
* Cached in the profile after the first lookup: it never changes for an
|
|
36
|
+
* account, and paying a round trip for it on every `env create` would be a
|
|
37
|
+
* waste. An explicit --org always wins.
|
|
38
|
+
*/
|
|
39
|
+
export declare function orgSlug(ctx: Context, explicit?: string): Promise<string>;
|
|
40
|
+
/**
|
|
41
|
+
* Run a command body and turn whatever it throws into an exit code.
|
|
42
|
+
*
|
|
43
|
+
* The distinctions that matter to someone scripting this: 3 means the
|
|
44
|
+
* credential is the problem (log in again), 4 means the plan is (upgrade), 1
|
|
45
|
+
* means the request itself failed. Anything else would make a CI job unable to
|
|
46
|
+
* tell "my token expired" from "that environment doesn't exist".
|
|
47
|
+
*/
|
|
48
|
+
export declare function run(body: () => Promise<void>): (...args: never[]) => Promise<void>;
|
|
49
|
+
/** The exit code for an Agent Designer refusal, or null when this is not one.
|
|
50
|
+
* A plan limit is a plan problem (4); owner-only is a plain failure (1). */
|
|
51
|
+
export declare function designerRefusal(err: QorenError): number | null;
|
|
52
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAIL,KAAK,OAAO,EACb,MAAM,aAAa,CAAC;AAarB,eAAO,MAAM,OAAO,UAAU,CAAC;AAE/B,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG;IACpB,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAIF;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAMzD;AAID;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAEnD;AAGD,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAoB9D;AAED;;;;;;GAMG;AACH,wBAAsB,OAAO,CAC3B,GAAG,EAAE,OAAO,EACZ,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CAMjB;AAED;;;;;;;GAOG;AACH,wBAAgB,GAAG,CACjB,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GACxB,CAAC,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAQrC;AAiED;4EAC4E;AAC5E,wBAAgB,eAAe,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAW9D"}
|
package/dist/context.js
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { Qoren, QorenError } from "@qoren/sdk";
|
|
2
|
+
import { patchProfile, profileName, resolveProfile, } from "./config.js";
|
|
3
|
+
import { EXIT_AUTH, EXIT_ERROR, EXIT_PAYMENT, fail, isJsonMode, } from "./output.js";
|
|
4
|
+
import { issuesFromBody, printIssues } from "./issues.js";
|
|
5
|
+
// What every command needs: an authenticated client, and one place that turns a
|
|
6
|
+
// control-plane failure into an exit code and a sentence worth reading.
|
|
7
|
+
export const VERSION = "0.1.0";
|
|
8
|
+
class AuthRequired extends Error {
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The org's plan does not include API access (a 403 with `code:
|
|
12
|
+
* "api_access_required"`). The token is fine, so this is a plan problem (exit
|
|
13
|
+
* 4), not a credential one (exit 3): logging in again would fail identically.
|
|
14
|
+
*
|
|
15
|
+
* Read off the code rather than the SDK's `isApiAccessRequired`, so a CLI
|
|
16
|
+
* built against an older SDK still tells the two apart.
|
|
17
|
+
*/
|
|
18
|
+
export function isApiAccessRequired(err) {
|
|
19
|
+
return (err instanceof QorenError &&
|
|
20
|
+
err.status === 403 &&
|
|
21
|
+
err.code === API_ACCESS_REQUIRED);
|
|
22
|
+
}
|
|
23
|
+
const API_ACCESS_REQUIRED = "api_access_required";
|
|
24
|
+
/**
|
|
25
|
+
* The caller's organization is being deleted (a 403 with `code:
|
|
26
|
+
* "org_deleting"`). Logging in again cannot help, so the server's message,
|
|
27
|
+
* which says what is happening, is shown instead of "credentials rejected".
|
|
28
|
+
*/
|
|
29
|
+
export function isOrgDeleting(err) {
|
|
30
|
+
return err instanceof QorenError && err.status === 403 && err.code === "org_deleting";
|
|
31
|
+
}
|
|
32
|
+
const PRICING_URL = "https://qoren.sh/pricing";
|
|
33
|
+
/** Build the client for this invocation, or explain how to get one. */
|
|
34
|
+
export function requireContext(options) {
|
|
35
|
+
const name = profileName(options.profile);
|
|
36
|
+
const profile = resolveProfile({
|
|
37
|
+
...(options.profile !== undefined ? { profile: options.profile } : {}),
|
|
38
|
+
...(options.apiUrl !== undefined ? { baseUrl: options.apiUrl } : {}),
|
|
39
|
+
});
|
|
40
|
+
if (!profile) {
|
|
41
|
+
throw new AuthRequired("You are not signed in. Run `qoren login`, or set QOREN_TOKEN.");
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
qoren: new Qoren({
|
|
45
|
+
baseUrl: profile.baseUrl,
|
|
46
|
+
token: profile.token,
|
|
47
|
+
userAgent: `qoren-cli/${VERSION}`,
|
|
48
|
+
}),
|
|
49
|
+
profile,
|
|
50
|
+
profileName: name,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The org's fleet tenant slug, which creating an environment needs.
|
|
55
|
+
*
|
|
56
|
+
* Cached in the profile after the first lookup: it never changes for an
|
|
57
|
+
* account, and paying a round trip for it on every `env create` would be a
|
|
58
|
+
* waste. An explicit --org always wins.
|
|
59
|
+
*/
|
|
60
|
+
export async function orgSlug(ctx, explicit) {
|
|
61
|
+
if (explicit)
|
|
62
|
+
return explicit;
|
|
63
|
+
if (ctx.profile.orgSlug)
|
|
64
|
+
return ctx.profile.orgSlug;
|
|
65
|
+
const slug = await ctx.qoren.resolveOrgSlug();
|
|
66
|
+
patchProfile(ctx.profileName, { orgSlug: slug });
|
|
67
|
+
return slug;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Run a command body and turn whatever it throws into an exit code.
|
|
71
|
+
*
|
|
72
|
+
* The distinctions that matter to someone scripting this: 3 means the
|
|
73
|
+
* credential is the problem (log in again), 4 means the plan is (upgrade), 1
|
|
74
|
+
* means the request itself failed. Anything else would make a CI job unable to
|
|
75
|
+
* tell "my token expired" from "that environment doesn't exist".
|
|
76
|
+
*/
|
|
77
|
+
export function run(body) {
|
|
78
|
+
return async () => {
|
|
79
|
+
try {
|
|
80
|
+
await body();
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
process.exitCode = report(err);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function report(err) {
|
|
88
|
+
if (err instanceof AuthRequired) {
|
|
89
|
+
fail(err.message);
|
|
90
|
+
return EXIT_AUTH;
|
|
91
|
+
}
|
|
92
|
+
if (err instanceof QorenError) {
|
|
93
|
+
// Checked before isAuthError: it is a 403, but signing in again is not the
|
|
94
|
+
// fix. The server's message names the plans that include API access.
|
|
95
|
+
// (A boolean, not a type guard: a guard would narrow QorenError out of
|
|
96
|
+
// every branch below.)
|
|
97
|
+
if (isApiAccessRequired(err)) {
|
|
98
|
+
fail(err.message ||
|
|
99
|
+
`Your plan does not include API access. Upgrade at ${PRICING_URL}.`, isJsonMode() ? err.body : undefined);
|
|
100
|
+
return EXIT_PAYMENT;
|
|
101
|
+
}
|
|
102
|
+
// Agent Designer refusals. They are 403s, but the credential is fine: the
|
|
103
|
+
// plan has no Custom agents (or no more of them), or only the owner may
|
|
104
|
+
// make this change. Sending someone to sign in again would be wrong.
|
|
105
|
+
const designer = designerRefusal(err);
|
|
106
|
+
if (designer !== null) {
|
|
107
|
+
fail(err.message, isJsonMode() ? err.body : undefined);
|
|
108
|
+
return designer;
|
|
109
|
+
}
|
|
110
|
+
if (err.isAuthError) {
|
|
111
|
+
fail(err.status === 401
|
|
112
|
+
? "Your credentials were rejected. Run `qoren login` again, or check QOREN_TOKEN."
|
|
113
|
+
: err.message);
|
|
114
|
+
return EXIT_AUTH;
|
|
115
|
+
}
|
|
116
|
+
if (err.isPaymentRequired) {
|
|
117
|
+
fail("This action needs an active plan. Open Qoren in a browser to choose one.");
|
|
118
|
+
return EXIT_PAYMENT;
|
|
119
|
+
}
|
|
120
|
+
// A 422 from a create carries the safety review's findings. They are the
|
|
121
|
+
// whole reason the call was refused, so print them rather than the summary
|
|
122
|
+
// line alone.
|
|
123
|
+
const findings = safetyFindings(err.body);
|
|
124
|
+
fail(err.message, isJsonMode() ? err.body : undefined);
|
|
125
|
+
// A design refused for its problems (422 validation_failed) lists them.
|
|
126
|
+
const issues = issuesFromBody(err.body);
|
|
127
|
+
if (!isJsonMode() && issues.length > 0)
|
|
128
|
+
printIssues(issues);
|
|
129
|
+
if (!isJsonMode() && findings.length > 0) {
|
|
130
|
+
for (const finding of findings) {
|
|
131
|
+
process.stderr.write(` ${finding.severity}: ${finding.title}\n ${finding.suggestion}\n`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return EXIT_ERROR;
|
|
135
|
+
}
|
|
136
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
137
|
+
return EXIT_ERROR;
|
|
138
|
+
}
|
|
139
|
+
/** The exit code for an Agent Designer refusal, or null when this is not one.
|
|
140
|
+
* A plan limit is a plan problem (4); owner-only is a plain failure (1). */
|
|
141
|
+
export function designerRefusal(err) {
|
|
142
|
+
if (err.status !== 403)
|
|
143
|
+
return null;
|
|
144
|
+
switch (err.code) {
|
|
145
|
+
case "designer_disabled":
|
|
146
|
+
case "custom_agent_limit":
|
|
147
|
+
return EXIT_PAYMENT;
|
|
148
|
+
case "designer_owner_only":
|
|
149
|
+
return EXIT_ERROR;
|
|
150
|
+
default:
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function safetyFindings(body) {
|
|
155
|
+
if (body &&
|
|
156
|
+
typeof body === "object" &&
|
|
157
|
+
"safetyFindings" in body &&
|
|
158
|
+
Array.isArray(body.safetyFindings)) {
|
|
159
|
+
return body.safetyFindings;
|
|
160
|
+
}
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seconds for a duration like `30m`, `2h` or `3d`, or null when it is not one.
|
|
3
|
+
*
|
|
4
|
+
* A bare number is refused rather than guessed at: "7" could be days or seconds
|
|
5
|
+
* depending on what the reader had in mind, and getting that wrong on a public
|
|
6
|
+
* link is the difference between a week and a moment.
|
|
7
|
+
*/
|
|
8
|
+
export declare function parseDuration(input: string): number | null;
|
|
9
|
+
//# sourceMappingURL=duration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duration.d.ts","sourceRoot":"","sources":["../src/duration.ts"],"names":[],"mappings":"AAeA;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM1D"}
|
package/dist/duration.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Reading a human duration off the command line.
|
|
2
|
+
//
|
|
3
|
+
// The API takes seconds, because a client has no reason to produce or parse
|
|
4
|
+
// "3d". A person at a terminal has every reason not to type 259200, so the flag
|
|
5
|
+
// takes the short form and this turns it into the number the wire wants. Same
|
|
6
|
+
// vocabulary the agent-facing files_share tool accepts, so a duration copied
|
|
7
|
+
// from the docs works in either place.
|
|
8
|
+
const UNITS = {
|
|
9
|
+
s: 1,
|
|
10
|
+
m: 60,
|
|
11
|
+
h: 3600,
|
|
12
|
+
d: 86400,
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Seconds for a duration like `30m`, `2h` or `3d`, or null when it is not one.
|
|
16
|
+
*
|
|
17
|
+
* A bare number is refused rather than guessed at: "7" could be days or seconds
|
|
18
|
+
* depending on what the reader had in mind, and getting that wrong on a public
|
|
19
|
+
* link is the difference between a week and a moment.
|
|
20
|
+
*/
|
|
21
|
+
export function parseDuration(input) {
|
|
22
|
+
const match = /^\s*(\d+)\s*([smhd])\s*$/i.exec(input);
|
|
23
|
+
if (!match)
|
|
24
|
+
return null;
|
|
25
|
+
const amount = Number.parseInt(match[1], 10);
|
|
26
|
+
if (!Number.isFinite(amount) || amount <= 0)
|
|
27
|
+
return null;
|
|
28
|
+
return amount * UNITS[match[2].toLowerCase()];
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=duration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duration.js","sourceRoot":"","sources":["../src/duration.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,EAAE;AACF,4EAA4E;AAC5E,gFAAgF;AAChF,8EAA8E;AAC9E,6EAA6E;AAC7E,uCAAuC;AAEvC,MAAM,KAAK,GAA2B;IACpC,CAAC,EAAE,CAAC;IACJ,CAAC,EAAE,EAAE;IACL,CAAC,EAAE,IAAI;IACP,CAAC,EAAE,KAAK;CACT,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,MAAM,KAAK,GAAG,2BAA2B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtD,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,EAAE,CAAC,CAAC;IAC9C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACzD,OAAO,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,WAAW,EAAE,CAAE,CAAC;AAClD,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { argvForInteractive, buildProgram } from "./program.js";
|
|
3
|
+
import { EXIT_ERROR, EXIT_USAGE, fail } from "./output.js";
|
|
4
|
+
// The `qoren` binary. The command tree lives in program.ts; this only runs it
|
|
5
|
+
// and decides what the shell sees.
|
|
6
|
+
async function main() {
|
|
7
|
+
try {
|
|
8
|
+
await buildProgram().parseAsync(argvForInteractive(process.argv));
|
|
9
|
+
}
|
|
10
|
+
catch (err) {
|
|
11
|
+
const code = err.code;
|
|
12
|
+
// Commander signals --help and --version by throwing. Neither is a failure.
|
|
13
|
+
if (code === "commander.helpDisplayed" || code === "commander.version") {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (code?.startsWith("commander.")) {
|
|
17
|
+
process.exitCode = EXIT_USAGE;
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
// A command body handles its own errors (see context.run); anything
|
|
21
|
+
// arriving here is unexpected, so report it rather than dying silently.
|
|
22
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
23
|
+
process.exitCode = EXIT_ERROR;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
void main();
|
|
27
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAE3D,8EAA8E;AAC9E,mCAAmC;AAEnC,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC;QACH,MAAM,YAAY,EAAE,CAAC,UAAU,CAAC,kBAAkB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACpE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,GAAI,GAAyB,CAAC,IAAI,CAAC;QAC7C,4EAA4E;QAC5E,IAAI,IAAI,KAAK,yBAAyB,IAAI,IAAI,KAAK,mBAAmB,EAAE,CAAC;YACvE,OAAO;QACT,CAAC;QACD,IAAI,IAAI,EAAE,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YACnC,OAAO,CAAC,QAAQ,GAAG,UAAU,CAAC;YAC9B,OAAO;QACT,CAAC;QACD,oEAAoE;QACpE,wEAAwE;QACxE,IAAI,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QACvD,OAAO,CAAC,QAAQ,GAAG,UAAU,CAAC;IAChC,CAAC;AACH,CAAC;AAED,KAAK,IAAI,EAAE,CAAC"}
|
package/dist/issues.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { DesignIssue } from "@qoren/sdk";
|
|
2
|
+
/** The issues a 422 body carries, or an empty list. */
|
|
3
|
+
export declare function issuesFromBody(body: unknown): DesignIssue[];
|
|
4
|
+
export declare function hasErrors(issues: DesignIssue[]): boolean;
|
|
5
|
+
/** One line per issue, errors first: severity, where, message, code. */
|
|
6
|
+
export declare function formatIssue(issue: DesignIssue): string;
|
|
7
|
+
export declare function printIssues(issues: DesignIssue[]): void;
|
|
8
|
+
/** "2 errors, 1 warning", or "no problems". */
|
|
9
|
+
export declare function issueSummary(issues: DesignIssue[]): string;
|
|
10
|
+
//# sourceMappingURL=issues.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"issues.d.ts","sourceRoot":"","sources":["../src/issues.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAU9C,uDAAuD;AACvD,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,WAAW,EAAE,CAe3D;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,CAExD;AAED,wEAAwE;AACxE,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAKtD;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,IAAI,CAMvD;AAED,+CAA+C;AAC/C,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,CAQ1D"}
|
package/dist/issues.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import pc from "picocolors";
|
|
2
|
+
// Showing what is wrong with a design.
|
|
3
|
+
//
|
|
4
|
+
// Import, publish, deploy and test runs all answer with the same list of
|
|
5
|
+
// issues, either as data (a push) or inside a 422 (a publish refused). One
|
|
6
|
+
// renderer, so a problem reads the same whichever command found it. Issues are
|
|
7
|
+
// commentary on the result, so they go to stderr like every other note.
|
|
8
|
+
/** The issues a 422 body carries, or an empty list. */
|
|
9
|
+
export function issuesFromBody(body) {
|
|
10
|
+
if (body &&
|
|
11
|
+
typeof body === "object" &&
|
|
12
|
+
"issues" in body &&
|
|
13
|
+
Array.isArray(body.issues)) {
|
|
14
|
+
return body.issues.filter((i) => !!i &&
|
|
15
|
+
typeof i === "object" &&
|
|
16
|
+
typeof i.message === "string");
|
|
17
|
+
}
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
export function hasErrors(issues) {
|
|
21
|
+
return issues.some((i) => i.severity === "error");
|
|
22
|
+
}
|
|
23
|
+
/** One line per issue, errors first: severity, where, message, code. */
|
|
24
|
+
export function formatIssue(issue) {
|
|
25
|
+
const where = [issue.nodeId, issue.path].filter(Boolean).join(" ");
|
|
26
|
+
const severity = issue.severity === "error" ? pc.red("error ") : pc.yellow("warning");
|
|
27
|
+
return ` ${severity} ${where ? `${pc.bold(where)}: ` : ""}${issue.message} ${pc.dim(`(${issue.code})`)}`;
|
|
28
|
+
}
|
|
29
|
+
export function printIssues(issues) {
|
|
30
|
+
const sorted = [...issues].sort((a, b) => (a.severity === "error" ? 0 : 1) - (b.severity === "error" ? 0 : 1));
|
|
31
|
+
for (const issue of sorted)
|
|
32
|
+
process.stderr.write(`${formatIssue(issue)}\n`);
|
|
33
|
+
}
|
|
34
|
+
/** "2 errors, 1 warning", or "no problems". */
|
|
35
|
+
export function issueSummary(issues) {
|
|
36
|
+
const errors = issues.filter((i) => i.severity === "error").length;
|
|
37
|
+
const warnings = issues.length - errors;
|
|
38
|
+
if (issues.length === 0)
|
|
39
|
+
return "no problems";
|
|
40
|
+
const part = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
|
|
41
|
+
return [errors ? part(errors, "error") : "", warnings ? part(warnings, "warning") : ""]
|
|
42
|
+
.filter(Boolean)
|
|
43
|
+
.join(", ");
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=issues.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"issues.js","sourceRoot":"","sources":["../src/issues.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Job } from "@qoren/sdk";
|
|
2
|
+
import type { Context } from "./context.js";
|
|
3
|
+
/**
|
|
4
|
+
* Await a job, printing each step as it begins. Returns the finished job.
|
|
5
|
+
*
|
|
6
|
+
* Silent in --json mode: the caller emits the result as data, and progress
|
|
7
|
+
* chatter on stdout would corrupt it (see output.ts).
|
|
8
|
+
*/
|
|
9
|
+
export declare function followJob(ctx: Context, jobId: string, headline?: string): Promise<Job>;
|
|
10
|
+
/** Step completion for `jobs ls`, e.g. "9/12". */
|
|
11
|
+
export declare function jobSummary(job: Job): string;
|
|
12
|
+
//# sourceMappingURL=jobProgress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jobProgress.d.ts","sourceRoot":"","sources":["../src/jobProgress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAW,MAAM,YAAY,CAAC;AAC/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAe5C;;;;;GAKG;AACH,wBAAsB,SAAS,CAC7B,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,MAAM,EACb,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,GAAG,CAAC,CA+Bd;AAED,kDAAkD;AAClD,wBAAgB,UAAU,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAG3C"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { dim, isJsonMode, note, statusColor } from "./output.js";
|
|
2
|
+
// Following a job in a terminal.
|
|
3
|
+
//
|
|
4
|
+
// Deploying an agent is twelve steps over SSH and takes minutes. Printing
|
|
5
|
+
// nothing for that long looks like a hang, and redrawing a live table breaks
|
|
6
|
+
// the moment output is piped to a file. So: one line per step, printed once,
|
|
7
|
+
// when that step starts. It reads as a log, works in CI, and never lies about
|
|
8
|
+
// where the job has got to.
|
|
9
|
+
function stepLabel(step) {
|
|
10
|
+
return step.label || step.key;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Await a job, printing each step as it begins. Returns the finished job.
|
|
14
|
+
*
|
|
15
|
+
* Silent in --json mode: the caller emits the result as data, and progress
|
|
16
|
+
* chatter on stdout would corrupt it (see output.ts).
|
|
17
|
+
*/
|
|
18
|
+
export async function followJob(ctx, jobId, headline) {
|
|
19
|
+
if (isJsonMode())
|
|
20
|
+
return ctx.qoren.jobs.await(jobId);
|
|
21
|
+
if (headline)
|
|
22
|
+
note(headline);
|
|
23
|
+
const announced = new Set();
|
|
24
|
+
let disconnected = false;
|
|
25
|
+
const job = await ctx.qoren.jobs.await(jobId, (snapshot) => {
|
|
26
|
+
if (!snapshot) {
|
|
27
|
+
// A retryable outage swallowed the poll. Say so once rather than every
|
|
28
|
+
// 2.5 seconds, and say it as "reconnecting" — the job itself is durable
|
|
29
|
+
// and still there.
|
|
30
|
+
if (!disconnected) {
|
|
31
|
+
disconnected = true;
|
|
32
|
+
note(dim(" reconnecting…"));
|
|
33
|
+
}
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
disconnected = false;
|
|
37
|
+
for (const step of snapshot.steps) {
|
|
38
|
+
if (announced.has(step.key))
|
|
39
|
+
continue;
|
|
40
|
+
// Only announce a step once it has actually started; a queued step is
|
|
41
|
+
// not progress.
|
|
42
|
+
if (step.status === "Pending")
|
|
43
|
+
continue;
|
|
44
|
+
announced.add(step.key);
|
|
45
|
+
note(` ${dim("·")} ${stepLabel(step)}`);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
note(` ${statusColor("Succeeded")} ${dim(`(job ${jobId})`)}`);
|
|
49
|
+
return job;
|
|
50
|
+
}
|
|
51
|
+
/** Step completion for `jobs ls`, e.g. "9/12". */
|
|
52
|
+
export function jobSummary(job) {
|
|
53
|
+
const done = job.steps.filter((s) => s.status === "Succeeded").length;
|
|
54
|
+
return job.steps.length > 0 ? `${done}/${job.steps.length}` : "";
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=jobProgress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jobProgress.js","sourceRoot":"","sources":["../src/jobProgress.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEjE,iCAAiC;AACjC,EAAE;AACF,0EAA0E;AAC1E,6EAA6E;AAC7E,6EAA6E;AAC7E,8EAA8E;AAC9E,4BAA4B;AAE5B,SAAS,SAAS,CAAC,IAAa;IAC9B,OAAO,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC;AAChC,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,GAAY,EACZ,KAAa,EACb,QAAiB;IAEjB,IAAI,UAAU,EAAE;QAAE,OAAO,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAErD,IAAI,QAAQ;QAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC7B,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IACpC,IAAI,YAAY,GAAG,KAAK,CAAC;IAEzB,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,EAAE;QACzD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,uEAAuE;YACvE,wEAAwE;YACxE,mBAAmB;YACnB,IAAI,CAAC,YAAY,EAAE,CAAC;gBAClB,YAAY,GAAG,IAAI,CAAC;gBACpB,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC;YAC/B,CAAC;YACD,OAAO;QACT,CAAC;QACD,YAAY,GAAG,KAAK,CAAC;QACrB,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;YAClC,IAAI,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC;gBAAE,SAAS;YACtC,sEAAsE;YACtE,gBAAgB;YAChB,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;gBAAE,SAAS;YACxC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACxB,IAAI,CAAC,KAAK,GAAG,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,KAAK,WAAW,CAAC,WAAW,CAAC,IAAI,GAAG,CAAC,QAAQ,KAAK,GAAG,CAAC,EAAE,CAAC,CAAC;IAC/D,OAAO,GAAG,CAAC;AACb,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,UAAU,CAAC,GAAQ;IACjC,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,MAAM,CAAC;IACtE,OAAO,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AACnE,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type PkcePair = {
|
|
2
|
+
verifier: string;
|
|
3
|
+
challenge: string;
|
|
4
|
+
};
|
|
5
|
+
export declare function createPkcePair(): PkcePair;
|
|
6
|
+
export type LoopbackResult = {
|
|
7
|
+
code: string;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Serve one redirect on a loopback port and resolve with the code it carries.
|
|
11
|
+
*
|
|
12
|
+
* `onReady` receives the redirect URI once the port is known, so the caller can
|
|
13
|
+
* build the authorize URL and open it. The server always shuts down: on
|
|
14
|
+
* success, on a mismatched state, and on timeout.
|
|
15
|
+
*/
|
|
16
|
+
export declare function awaitLoopbackCode(options: {
|
|
17
|
+
state: string;
|
|
18
|
+
timeoutMs: number;
|
|
19
|
+
onReady: (redirectUri: string) => void | Promise<void>;
|
|
20
|
+
}): Promise<LoopbackResult>;
|
|
21
|
+
/**
|
|
22
|
+
* Open a URL in the user's browser, best effort.
|
|
23
|
+
*
|
|
24
|
+
* Returns false when we could not, which is not an error: the caller prints the
|
|
25
|
+
* URL either way, and on a headless box printing it is the whole answer.
|
|
26
|
+
*/
|
|
27
|
+
export declare function openBrowser(url: string): boolean;
|
|
28
|
+
//# sourceMappingURL=loopback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loopback.d.ts","sourceRoot":"","sources":["../src/loopback.ts"],"names":[],"mappings":"AAiBA,MAAM,MAAM,QAAQ,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/D,wBAAgB,cAAc,IAAI,QAAQ,CAMzC;AAaD,MAAM,MAAM,cAAc,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9C;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxD,GAAG,OAAO,CAAC,cAAc,CAAC,CAyE1B;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAkBhD"}
|
package/dist/loopback.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
2
|
+
import { createServer } from "node:http";
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
export function createPkcePair() {
|
|
5
|
+
const verifier = randomBytes(32).toString("base64url");
|
|
6
|
+
return {
|
|
7
|
+
verifier,
|
|
8
|
+
challenge: createHash("sha256").update(verifier).digest("base64url"),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
const PAGE = (title, body) => `<!doctype html>
|
|
12
|
+
<html lang="en"><head><meta charset="utf-8"><title>${title}</title>
|
|
13
|
+
<style>
|
|
14
|
+
body{font:16px/1.6 system-ui,sans-serif;margin:0;display:grid;place-items:center;
|
|
15
|
+
min-height:100vh;background:#0b0d10;color:#e6e8eb}
|
|
16
|
+
main{max-width:32rem;padding:2rem;text-align:center}
|
|
17
|
+
h1{font-size:1.25rem;margin:0 0 .5rem}
|
|
18
|
+
p{margin:0;color:#9aa4b2}
|
|
19
|
+
</style></head>
|
|
20
|
+
<body><main><h1>${title}</h1><p>${body}</p></main></body></html>`;
|
|
21
|
+
/**
|
|
22
|
+
* Serve one redirect on a loopback port and resolve with the code it carries.
|
|
23
|
+
*
|
|
24
|
+
* `onReady` receives the redirect URI once the port is known, so the caller can
|
|
25
|
+
* build the authorize URL and open it. The server always shuts down: on
|
|
26
|
+
* success, on a mismatched state, and on timeout.
|
|
27
|
+
*/
|
|
28
|
+
export async function awaitLoopbackCode(options) {
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
let settled = false;
|
|
31
|
+
const server = createServer((req, res) => {
|
|
32
|
+
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
33
|
+
const code = url.searchParams.get("code");
|
|
34
|
+
const state = url.searchParams.get("state");
|
|
35
|
+
const done = (status, title, body) => {
|
|
36
|
+
res.writeHead(status, { "content-type": "text/html; charset=utf-8" });
|
|
37
|
+
res.end(PAGE(title, body));
|
|
38
|
+
};
|
|
39
|
+
if (!code || !state) {
|
|
40
|
+
done(400, "Something went wrong", "No authorization code arrived.");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
// A state mismatch means this redirect is not the one we started. Refuse
|
|
44
|
+
// it rather than accepting a code we cannot account for.
|
|
45
|
+
if (state !== options.state) {
|
|
46
|
+
done(400, "Something went wrong", "This response did not match the request.");
|
|
47
|
+
finish(() => reject(new Error("The sign-in response did not match the request.")));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
done(200, "You're signed in", "You can close this tab and return to your terminal.");
|
|
51
|
+
finish(() => resolve({ code }));
|
|
52
|
+
});
|
|
53
|
+
function finish(action) {
|
|
54
|
+
if (settled)
|
|
55
|
+
return;
|
|
56
|
+
settled = true;
|
|
57
|
+
clearTimeout(timer);
|
|
58
|
+
// Let the response flush before tearing the socket down.
|
|
59
|
+
setTimeout(() => server.close(), 100);
|
|
60
|
+
action();
|
|
61
|
+
}
|
|
62
|
+
const timer = setTimeout(() => {
|
|
63
|
+
finish(() => reject(new Error("Timed out waiting for the browser. Run `qoren login` again, or use `qoren login --token`.")));
|
|
64
|
+
}, options.timeoutMs);
|
|
65
|
+
server.on("error", (err) => finish(() => reject(err)));
|
|
66
|
+
// Port 0 = let the OS pick. 127.0.0.1 rather than localhost so we bind one
|
|
67
|
+
// known interface instead of whatever the resolver returns.
|
|
68
|
+
server.listen(0, "127.0.0.1", () => {
|
|
69
|
+
const address = server.address();
|
|
70
|
+
if (!address) {
|
|
71
|
+
finish(() => reject(new Error("Could not open a local port.")));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
void (async () => {
|
|
75
|
+
try {
|
|
76
|
+
await options.onReady(`http://127.0.0.1:${address.port}/callback`);
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
finish(() => reject(err instanceof Error ? err : new Error(String(err))));
|
|
80
|
+
}
|
|
81
|
+
})();
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Open a URL in the user's browser, best effort.
|
|
87
|
+
*
|
|
88
|
+
* Returns false when we could not, which is not an error: the caller prints the
|
|
89
|
+
* URL either way, and on a headless box printing it is the whole answer.
|
|
90
|
+
*/
|
|
91
|
+
export function openBrowser(url) {
|
|
92
|
+
const command = process.platform === "darwin"
|
|
93
|
+
? "open"
|
|
94
|
+
: process.platform === "win32"
|
|
95
|
+
? "cmd"
|
|
96
|
+
: "xdg-open";
|
|
97
|
+
const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
98
|
+
try {
|
|
99
|
+
const child = spawn(command, args, { stdio: "ignore", detached: true });
|
|
100
|
+
child.on("error", () => {
|
|
101
|
+
/* No browser here; the printed URL is the fallback. */
|
|
102
|
+
});
|
|
103
|
+
child.unref();
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=loopback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loopback.js","sourceRoot":"","sources":["../src/loopback.ts"],"names":[],"mappings":"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"}
|