@ttctl/cli 0.1.0-rc.6 → 0.1.0-rc.8
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/commands/applications/availability-request.d.ts +47 -0
- package/dist/commands/applications/availability-request.d.ts.map +1 -0
- package/dist/commands/applications/availability-request.js +98 -0
- package/dist/commands/applications/availability-request.js.map +1 -0
- package/dist/commands/applications/confirm.d.ts +32 -2
- package/dist/commands/applications/confirm.d.ts.map +1 -1
- package/dist/commands/applications/confirm.js +194 -1
- package/dist/commands/applications/confirm.js.map +1 -1
- package/dist/commands/applications/index.d.ts +6 -2
- package/dist/commands/applications/index.d.ts.map +1 -1
- package/dist/commands/applications/index.js +112 -3
- package/dist/commands/applications/index.js.map +1 -1
- package/dist/commands/applications/interview.d.ts +159 -0
- package/dist/commands/applications/interview.d.ts.map +1 -0
- package/dist/commands/applications/interview.js +396 -0
- package/dist/commands/applications/interview.js.map +1 -0
- package/dist/commands/jobs/apply.d.ts +194 -0
- package/dist/commands/jobs/apply.d.ts.map +1 -0
- package/dist/commands/jobs/apply.js +505 -0
- package/dist/commands/jobs/apply.js.map +1 -0
- package/dist/commands/jobs/index.d.ts +19 -5
- package/dist/commands/jobs/index.d.ts.map +1 -1
- package/dist/commands/jobs/index.js +78 -6
- package/dist/commands/jobs/index.js.map +1 -1
- package/dist/commands/jobs/show.d.ts +63 -2
- package/dist/commands/jobs/show.d.ts.map +1 -1
- package/dist/commands/jobs/show.js +77 -5
- package/dist/commands/jobs/show.js.map +1 -1
- package/dist/commands/payments/index.d.ts +5 -2
- package/dist/commands/payments/index.d.ts.map +1 -1
- package/dist/commands/payments/index.js +27 -4
- package/dist/commands/payments/index.js.map +1 -1
- package/dist/commands/payments/rate.d.ts +16 -0
- package/dist/commands/payments/rate.d.ts.map +1 -1
- package/dist/commands/payments/rate.js +30 -0
- package/dist/commands/payments/rate.js.map +1 -1
- package/dist/commands/payments/summary.d.ts +17 -0
- package/dist/commands/payments/summary.d.ts.map +1 -0
- package/dist/commands/payments/summary.js +42 -0
- package/dist/commands/payments/summary.js.map +1 -0
- package/dist/commands/profile/employment/index.d.ts.map +1 -1
- package/dist/commands/profile/employment/index.js +23 -3
- package/dist/commands/profile/employment/index.js.map +1 -1
- package/dist/commands/profile/reviews/index.d.ts.map +1 -1
- package/dist/commands/profile/reviews/index.js +5 -1
- package/dist/commands/profile/reviews/index.js.map +1 -1
- package/dist/commands/profile/reviews/submit-for-review.d.ts +8 -0
- package/dist/commands/profile/reviews/submit-for-review.d.ts.map +1 -1
- package/dist/commands/profile/reviews/submit-for-review.js +15 -1
- package/dist/commands/profile/reviews/submit-for-review.js.map +1 -1
- package/dist/lib/json-input.d.ts +121 -0
- package/dist/lib/json-input.d.ts.map +1 -0
- package/dist/lib/json-input.js +245 -0
- package/dist/lib/json-input.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { applications } from "@ttctl/core";
|
|
2
|
+
import type { OutputFormat } from "../../lib/output.js";
|
|
3
|
+
/**
|
|
4
|
+
* Action handler for `ttctl applications availability-request show <id>`
|
|
5
|
+
* (#442). Read-only fetch of one availability request by id. The id is
|
|
6
|
+
* the `AvailabilityRequest.id` from `ttctl applications show
|
|
7
|
+
* <activityId>` output (the `Availability request: <id>` line surfaced
|
|
8
|
+
* when the activity row has an associated AR) — the same id the
|
|
9
|
+
* `applications confirm` / `reject` write-side commands take.
|
|
10
|
+
*
|
|
11
|
+
* Pretty rendering is a sectioned multi-line block grouped into
|
|
12
|
+
* Availability request / Comment / Job. Sections with no data (e.g. no
|
|
13
|
+
* recruiter comment) are omitted.
|
|
14
|
+
*
|
|
15
|
+
* `json` / `yaml` always emit the full
|
|
16
|
+
* {@link applications.AvailabilityRequestDetail} projection — machine
|
|
17
|
+
* consumers project as needed.
|
|
18
|
+
*/
|
|
19
|
+
export declare function runApplicationsAvailabilityRequestShow(id: string, output: OutputFormat): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Render an {@link applications.AvailabilityRequestDetail} as a
|
|
22
|
+
* sectioned multi-line block. Pure — directly unit-testable.
|
|
23
|
+
*
|
|
24
|
+
* Layout:
|
|
25
|
+
*
|
|
26
|
+
* Availability request <id>
|
|
27
|
+
* Status: <status>
|
|
28
|
+
* Kind: <kind>
|
|
29
|
+
* Fixed rate: <verbose | $<decimal>/h> // omitted if fixedRate null
|
|
30
|
+
* Created: <createdAt>
|
|
31
|
+
* Updated: <updatedAt>
|
|
32
|
+
* Answered: <answeredAt> // omitted if null (pending)
|
|
33
|
+
*
|
|
34
|
+
* Comment // omitted if comment null/empty
|
|
35
|
+
* <recruiter note, paragraph-preserved>
|
|
36
|
+
*
|
|
37
|
+
* Job // omitted if job is null
|
|
38
|
+
* Job id: <id>
|
|
39
|
+
* Title: <title>
|
|
40
|
+
* URL: <url>
|
|
41
|
+
* Client: <fullName>
|
|
42
|
+
*
|
|
43
|
+
* Per-line null guards keep the header concise when the wire returns a
|
|
44
|
+
* sparse availability request.
|
|
45
|
+
*/
|
|
46
|
+
export declare function formatAvailabilityRequestDetail(item: applications.AvailabilityRequestDetail): string;
|
|
47
|
+
//# sourceMappingURL=availability-request.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"availability-request.d.ts","sourceRoot":"","sources":["../../../src/commands/applications/availability-request.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,sCAAsC,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAa5G;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,YAAY,CAAC,yBAAyB,GAAG,MAAM,CA+BpG"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright (C) 2026 Oleksii PELYKH
|
|
3
|
+
import { applications } from "@ttctl/core";
|
|
4
|
+
import { emitResult } from "../../lib/output.js";
|
|
5
|
+
import { formatFixedRate, handleApplicationsError, loadAuthTokenOrExit } from "./shared.js";
|
|
6
|
+
/**
|
|
7
|
+
* Action handler for `ttctl applications availability-request show <id>`
|
|
8
|
+
* (#442). Read-only fetch of one availability request by id. The id is
|
|
9
|
+
* the `AvailabilityRequest.id` from `ttctl applications show
|
|
10
|
+
* <activityId>` output (the `Availability request: <id>` line surfaced
|
|
11
|
+
* when the activity row has an associated AR) — the same id the
|
|
12
|
+
* `applications confirm` / `reject` write-side commands take.
|
|
13
|
+
*
|
|
14
|
+
* Pretty rendering is a sectioned multi-line block grouped into
|
|
15
|
+
* Availability request / Comment / Job. Sections with no data (e.g. no
|
|
16
|
+
* recruiter comment) are omitted.
|
|
17
|
+
*
|
|
18
|
+
* `json` / `yaml` always emit the full
|
|
19
|
+
* {@link applications.AvailabilityRequestDetail} projection — machine
|
|
20
|
+
* consumers project as needed.
|
|
21
|
+
*/
|
|
22
|
+
export async function runApplicationsAvailabilityRequestShow(id, output) {
|
|
23
|
+
const token = await loadAuthTokenOrExit("applications availability-request show", output);
|
|
24
|
+
let item;
|
|
25
|
+
try {
|
|
26
|
+
item = await applications.availabilityRequests.show(token, id);
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
handleApplicationsError("applications availability-request show", err, output);
|
|
30
|
+
}
|
|
31
|
+
emitResult(item, output, {
|
|
32
|
+
pretty: (data) => formatAvailabilityRequestDetail(data),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Render an {@link applications.AvailabilityRequestDetail} as a
|
|
37
|
+
* sectioned multi-line block. Pure — directly unit-testable.
|
|
38
|
+
*
|
|
39
|
+
* Layout:
|
|
40
|
+
*
|
|
41
|
+
* Availability request <id>
|
|
42
|
+
* Status: <status>
|
|
43
|
+
* Kind: <kind>
|
|
44
|
+
* Fixed rate: <verbose | $<decimal>/h> // omitted if fixedRate null
|
|
45
|
+
* Created: <createdAt>
|
|
46
|
+
* Updated: <updatedAt>
|
|
47
|
+
* Answered: <answeredAt> // omitted if null (pending)
|
|
48
|
+
*
|
|
49
|
+
* Comment // omitted if comment null/empty
|
|
50
|
+
* <recruiter note, paragraph-preserved>
|
|
51
|
+
*
|
|
52
|
+
* Job // omitted if job is null
|
|
53
|
+
* Job id: <id>
|
|
54
|
+
* Title: <title>
|
|
55
|
+
* URL: <url>
|
|
56
|
+
* Client: <fullName>
|
|
57
|
+
*
|
|
58
|
+
* Per-line null guards keep the header concise when the wire returns a
|
|
59
|
+
* sparse availability request.
|
|
60
|
+
*/
|
|
61
|
+
export function formatAvailabilityRequestDetail(item) {
|
|
62
|
+
const lines = [];
|
|
63
|
+
lines.push(`Availability request ${item.id}`);
|
|
64
|
+
if (item.status !== null)
|
|
65
|
+
lines.push(` Status: ${item.status}`);
|
|
66
|
+
if (item.kind !== null)
|
|
67
|
+
lines.push(` Kind: ${item.kind}`);
|
|
68
|
+
if (item.fixedRate !== null)
|
|
69
|
+
lines.push(` Fixed rate: ${formatFixedRate(item.fixedRate)}`);
|
|
70
|
+
if (item.createdAt !== null)
|
|
71
|
+
lines.push(` Created: ${item.createdAt}`);
|
|
72
|
+
if (item.updatedAt !== null)
|
|
73
|
+
lines.push(` Updated: ${item.updatedAt}`);
|
|
74
|
+
if (item.answeredAt !== null)
|
|
75
|
+
lines.push(` Answered: ${item.answeredAt}`);
|
|
76
|
+
if (item.comment !== null && item.comment !== "") {
|
|
77
|
+
lines.push("");
|
|
78
|
+
lines.push("Comment");
|
|
79
|
+
for (const para of item.comment.split(/\n+/)) {
|
|
80
|
+
if (para.trim().length > 0)
|
|
81
|
+
lines.push(` ${para}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (item.job !== null) {
|
|
85
|
+
lines.push("");
|
|
86
|
+
lines.push("Job");
|
|
87
|
+
lines.push(` Job id: ${item.job.id}`);
|
|
88
|
+
if (item.job.title !== null && item.job.title !== "")
|
|
89
|
+
lines.push(` Title: ${item.job.title}`);
|
|
90
|
+
if (item.job.url !== null && item.job.url !== "")
|
|
91
|
+
lines.push(` URL: ${item.job.url}`);
|
|
92
|
+
if (item.job.client?.fullName != null && item.job.client.fullName !== "") {
|
|
93
|
+
lines.push(` Client: ${item.job.client.fullName}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return lines.join("\n");
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=availability-request.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"availability-request.js","sourceRoot":"","sources":["../../../src/commands/applications/availability-request.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,14 +1,36 @@
|
|
|
1
1
|
import { applications } from "@ttctl/core";
|
|
2
2
|
import type { OutputFormat } from "../../lib/output.js";
|
|
3
3
|
/**
|
|
4
|
-
* Options for `ttctl applications confirm <id>` (#411). Mirrors
|
|
5
|
-
* service-layer {@link applications.ConfirmInput} shape with CLI-side
|
|
4
|
+
* Options for `ttctl applications confirm <id>` (#411 + #428). Mirrors
|
|
5
|
+
* the service-layer {@link applications.ConfirmInput} shape with CLI-side
|
|
6
6
|
* argument validation.
|
|
7
|
+
*
|
|
8
|
+
* `answersFile` / `pitchFile` (#428) are CLI-only paths (or `-` for
|
|
9
|
+
* stdin) — the action handler reads + parses the JSON file BEFORE calling
|
|
10
|
+
* into the service, then forwards the parsed payloads as
|
|
11
|
+
* `matcherQuestionsAnswers` / `expertiseQuestionsAnswers` / `pitchInput`
|
|
12
|
+
* (the Stage-1 opaque pass-through; see ADR-008 § Decision Part 3).
|
|
7
13
|
*/
|
|
8
14
|
export interface ApplicationsConfirmOptions {
|
|
9
15
|
message?: string;
|
|
10
16
|
rate?: string;
|
|
11
17
|
kind?: applications.AvailabilityRequestKind;
|
|
18
|
+
/**
|
|
19
|
+
* Path to a JSON file (or `-` for stdin) containing
|
|
20
|
+
* `{ matcherAnswers: [...], expertiseAnswers: [...] }` per ADR-008
|
|
21
|
+
* § Decision Part 2. Parsed payload forwarded as
|
|
22
|
+
* `matcherQuestionsAnswers` / `expertiseQuestionsAnswers` to the
|
|
23
|
+
* service. Question identifiers come from `applications show
|
|
24
|
+
* <activityId>` output.
|
|
25
|
+
*/
|
|
26
|
+
answersFile?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Path to a JSON file (or `-` for stdin) containing a `PitchInput`
|
|
29
|
+
* object. Parsed payload forwarded as `pitchInput` (note: core field
|
|
30
|
+
* is `pitchInput`; wire variable is `$pitchInput` mapping to the
|
|
31
|
+
* mutation's `pitchData` input field — see ADR-008 § Decision Part 2).
|
|
32
|
+
*/
|
|
33
|
+
pitchFile?: string;
|
|
12
34
|
output: OutputFormat;
|
|
13
35
|
}
|
|
14
36
|
/**
|
|
@@ -29,6 +51,14 @@ export interface ApplicationsConfirmOptions {
|
|
|
29
51
|
* Flexible-kind ARs have no recruiter-pinned rate, so callers MUST
|
|
30
52
|
* pass `--rate <decimal>` when the AR is FLEXIBLE / MARKETPLACE_FLEXIBLE.
|
|
31
53
|
*
|
|
54
|
+
* Optional `--answers-file <path>` / `--pitch-file <path>` (#428) — see
|
|
55
|
+
* the option type {@link ApplicationsConfirmOptions} for the file
|
|
56
|
+
* grammar. Both flags accept `-` to read JSON from stdin per commander
|
|
57
|
+
* convention; per ADR-008 § Decision Part 2 only one flag may claim
|
|
58
|
+
* stdin per invocation. Files are read + parsed BEFORE any wire call;
|
|
59
|
+
* malformed JSON refuses with the `VALIDATION_ERROR` envelope and no
|
|
60
|
+
* mutation is issued.
|
|
61
|
+
*
|
|
32
62
|
* **DESTRUCTIVE** — confirming an IR transitions the AR to
|
|
33
63
|
* `AVAILABILITY_REQUEST_CONFIRMED` and creates a `JobApplication`. No
|
|
34
64
|
* withdraw operation is available on the wire. Prefer `--dry-run` to
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"confirm.d.ts","sourceRoot":"","sources":["../../../src/commands/applications/confirm.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"confirm.d.ts","sourceRoot":"","sources":["../../../src/commands/applications/confirm.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAM3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,0BAA0B;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,YAAY,CAAC,uBAAuB,CAAC;IAC5C;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,YAAY,CAAC;CACtB;AA+CD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAsB,sBAAsB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,0BAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CAkExG;AA8JD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,CAAC,iCAAiC,GAAG,MAAM,CAcnG"}
|
|
@@ -1,10 +1,34 @@
|
|
|
1
1
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
2
|
// Copyright (C) 2026 Oleksii PELYKH
|
|
3
3
|
import { applications } from "@ttctl/core";
|
|
4
|
+
import { z } from "zod";
|
|
4
5
|
import { getCliDryRun } from "../../lib/dry-run.js";
|
|
5
|
-
import { emitDryRunSuccess, emitUpdateSuccess } from "../../lib/envelopes.js";
|
|
6
|
+
import { emitDryRunSuccess, emitErrorAndExit, emitUpdateSuccess } from "../../lib/envelopes.js";
|
|
7
|
+
import { JsonInputError, parseAsRecovered, readJsonInput } from "../../lib/json-input.js";
|
|
6
8
|
import { handleApplicationsError, loadAuthTokenOrExit } from "./shared.js";
|
|
7
9
|
const DECIMAL_PATTERN = /^\d+(\.\d+)?$/;
|
|
10
|
+
/**
|
|
11
|
+
* Stage-2 Zod schemas for `--answers-file` payload validation (#438).
|
|
12
|
+
*
|
|
13
|
+
* Built ONCE at module load — the factory pattern in
|
|
14
|
+
* `__generated__/zod-schemas.ts` returns a fresh `z.object(...)` per
|
|
15
|
+
* call; we materialize each schema once and reuse it across parse
|
|
16
|
+
* invocations.
|
|
17
|
+
*
|
|
18
|
+
* `.strict()` is mandatory at this boundary: codegen emits schemas with
|
|
19
|
+
* default "strip unknown" semantics, which would silently pass extra
|
|
20
|
+
* keys. The AC requires "extra unknown key in payload rejected with
|
|
21
|
+
* field-path error" → wrap each inner schema with `.strict()`.
|
|
22
|
+
*
|
|
23
|
+
* Array wrappers (`z.array(...)`) live at the call site so each element
|
|
24
|
+
* is validated independently; per-entry failures surface their array
|
|
25
|
+
* index in the field path (`matcherAnswers[2].id: …`).
|
|
26
|
+
*/
|
|
27
|
+
const STRICT_MATCHER_ANSWER_SCHEMA = applications.JobPositionAnswerInputSchema().strict();
|
|
28
|
+
const STRICT_EXPERTISE_ANSWER_SCHEMA = applications.JobExpertiseAnswerInputSchema().strict();
|
|
29
|
+
const STRICT_PITCH_INPUT_SCHEMA = applications.PitchInputSchema().strict();
|
|
30
|
+
const MATCHER_ANSWERS_ARRAY_SCHEMA = z.array(STRICT_MATCHER_ANSWER_SCHEMA);
|
|
31
|
+
const EXPERTISE_ANSWERS_ARRAY_SCHEMA = z.array(STRICT_EXPERTISE_ANSWER_SCHEMA);
|
|
8
32
|
/**
|
|
9
33
|
* Action handler for `ttctl applications confirm <id>` (#411).
|
|
10
34
|
*
|
|
@@ -23,6 +47,14 @@ const DECIMAL_PATTERN = /^\d+(\.\d+)?$/;
|
|
|
23
47
|
* Flexible-kind ARs have no recruiter-pinned rate, so callers MUST
|
|
24
48
|
* pass `--rate <decimal>` when the AR is FLEXIBLE / MARKETPLACE_FLEXIBLE.
|
|
25
49
|
*
|
|
50
|
+
* Optional `--answers-file <path>` / `--pitch-file <path>` (#428) — see
|
|
51
|
+
* the option type {@link ApplicationsConfirmOptions} for the file
|
|
52
|
+
* grammar. Both flags accept `-` to read JSON from stdin per commander
|
|
53
|
+
* convention; per ADR-008 § Decision Part 2 only one flag may claim
|
|
54
|
+
* stdin per invocation. Files are read + parsed BEFORE any wire call;
|
|
55
|
+
* malformed JSON refuses with the `VALIDATION_ERROR` envelope and no
|
|
56
|
+
* mutation is issued.
|
|
57
|
+
*
|
|
26
58
|
* **DESTRUCTIVE** — confirming an IR transitions the AR to
|
|
27
59
|
* `AVAILABILITY_REQUEST_CONFIRMED` and creates a `JobApplication`. No
|
|
28
60
|
* withdraw operation is available on the wire. Prefer `--dry-run` to
|
|
@@ -42,6 +74,24 @@ export async function runApplicationsConfirm(id, opts) {
|
|
|
42
74
|
}
|
|
43
75
|
if (opts.kind !== undefined)
|
|
44
76
|
input.kind = opts.kind;
|
|
77
|
+
// Load + parse answers / pitch JSON BEFORE any wire call (#428). Any
|
|
78
|
+
// parse / file failure short-circuits with a `VALIDATION_ERROR`
|
|
79
|
+
// envelope; no mutation is issued. Both flags surface their typed
|
|
80
|
+
// `JsonInputError` codes through the SAME `VALIDATION_ERROR` envelope
|
|
81
|
+
// for consistency (the AC pins the envelope code, not the inner
|
|
82
|
+
// JsonInputError code).
|
|
83
|
+
if (opts.answersFile !== undefined) {
|
|
84
|
+
const payload = await loadJsonInputOrExit(opts.answersFile, "answers-file", opts.output);
|
|
85
|
+
const answers = narrowAnswersPayload(payload, opts.output);
|
|
86
|
+
if (answers.matcherAnswers !== undefined)
|
|
87
|
+
input.matcherQuestionsAnswers = answers.matcherAnswers;
|
|
88
|
+
if (answers.expertiseAnswers !== undefined)
|
|
89
|
+
input.expertiseQuestionsAnswers = answers.expertiseAnswers;
|
|
90
|
+
}
|
|
91
|
+
if (opts.pitchFile !== undefined) {
|
|
92
|
+
const payload = await loadJsonInputOrExit(opts.pitchFile, "pitch-file", opts.output);
|
|
93
|
+
input.pitchInput = narrowPitchPayload(payload, opts.output);
|
|
94
|
+
}
|
|
45
95
|
let outcome;
|
|
46
96
|
try {
|
|
47
97
|
outcome = await applications.confirm(token, id, input, { dryRun });
|
|
@@ -71,6 +121,149 @@ export async function runApplicationsConfirm(id, opts) {
|
|
|
71
121
|
prettyEntity: (data) => formatRespondPayload(data),
|
|
72
122
|
});
|
|
73
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* Read + parse a JSON file (or stdin) via {@link readJsonInput}. On any
|
|
126
|
+
* {@link JsonInputError} surface a structured `VALIDATION_ERROR` envelope
|
|
127
|
+
* and exit non-zero — the AC pins the envelope code, not the inner
|
|
128
|
+
* JsonInputError code (which appears in the message instead). Non-Error
|
|
129
|
+
* throws are re-raised as-is so the outer handler catches them. Returns
|
|
130
|
+
* the parsed value as `unknown` (Stage-1 opaque pass-through).
|
|
131
|
+
*/
|
|
132
|
+
async function loadJsonInputOrExit(rawPath, flagName, format) {
|
|
133
|
+
try {
|
|
134
|
+
return await readJsonInput(rawPath, { flagName });
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
if (err instanceof JsonInputError) {
|
|
138
|
+
emitErrorAndExit({
|
|
139
|
+
operation: "applications.confirm",
|
|
140
|
+
format,
|
|
141
|
+
errors: [{ code: "VALIDATION_ERROR", message: err.message, hint: hintForJsonInputCode(err.code) }],
|
|
142
|
+
prettySummary: `applications confirm failed (VALIDATION_ERROR): ${err.message}`,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
throw err;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Map a {@link JsonInputError} code to an actionable recovery hint
|
|
150
|
+
* surfaced through the `VALIDATION_ERROR` envelope's `hint:` field. The
|
|
151
|
+
* AC mandates "Recovery hint cites the parse failure line/column" for
|
|
152
|
+
* `PARSE_ERROR` — the line/column live in the `message` (formatted by
|
|
153
|
+
* {@link readJsonInput}), so the hint here surfaces the procedural
|
|
154
|
+
* recovery action.
|
|
155
|
+
*/
|
|
156
|
+
function hintForJsonInputCode(code) {
|
|
157
|
+
switch (code) {
|
|
158
|
+
case "FILE_NOT_FOUND":
|
|
159
|
+
return "Verify the path exists; the absolute path is in the error message.";
|
|
160
|
+
case "FILE_READ_ERROR":
|
|
161
|
+
return "Check filesystem permissions on the file (and its parent directory).";
|
|
162
|
+
case "PARSE_ERROR":
|
|
163
|
+
return "Fix the JSON syntax at the cited line/column; the file shape should be { matcherAnswers: [...], expertiseAnswers: [...] }.";
|
|
164
|
+
case "SCHEMA_ERROR":
|
|
165
|
+
return "Fix the payload to match the recovered shape — matcher answers use { id, answer }; expertise answers use { questionId, other, subjectId }; pitch uses PitchInput.";
|
|
166
|
+
case "STDIN_UNAVAILABLE":
|
|
167
|
+
return "Pipe JSON into stdin (e.g. `cat answers.json | ttctl ...`) or pass a file path.";
|
|
168
|
+
case "STDIN_DOUBLE_CLAIM":
|
|
169
|
+
return "Only one of --answers-file and --pitch-file may read from stdin per invocation; pass a file path for the other.";
|
|
170
|
+
default:
|
|
171
|
+
return "Inspect the input and retry.";
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Narrow the parsed JSON from `--answers-file` into the
|
|
176
|
+
* {@link AnswersFilePayload} shape. Surfaces a `VALIDATION_ERROR`
|
|
177
|
+
* envelope (NOT a wire call) when the top-level wrapper shape is wrong
|
|
178
|
+
* — e.g. a bare array, a string, or a non-object — OR when the inner
|
|
179
|
+
* arrays fail the recovered Zod schemas (per #438 Stage-2 tightening).
|
|
180
|
+
* Each inner schema is wrapped with `.strict()` so extra unknown keys
|
|
181
|
+
* surface as a field-path Zod issue (e.g. `matcherAnswers[2].questionId:
|
|
182
|
+
* unrecognized_keys (Unrecognized key(s) in object)`) — the AC's
|
|
183
|
+
* behavioral scenario 2 "Extra unknown key in payload rejected with
|
|
184
|
+
* field-path error".
|
|
185
|
+
*/
|
|
186
|
+
function narrowAnswersPayload(payload, format) {
|
|
187
|
+
if (payload === null || typeof payload !== "object" || Array.isArray(payload)) {
|
|
188
|
+
emitErrorAndExit({
|
|
189
|
+
operation: "applications.confirm",
|
|
190
|
+
format,
|
|
191
|
+
errors: [
|
|
192
|
+
{
|
|
193
|
+
code: "VALIDATION_ERROR",
|
|
194
|
+
message: "--answers-file: expected a JSON object with `matcherAnswers` and/or `expertiseAnswers` arrays.",
|
|
195
|
+
hint: "File shape: { matcherAnswers: [...], expertiseAnswers: [...] } — both keys are optional but the top-level value must be an object.",
|
|
196
|
+
},
|
|
197
|
+
],
|
|
198
|
+
prettySummary: "applications confirm failed (VALIDATION_ERROR): --answers-file shape is not a JSON object.",
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
const record = payload;
|
|
202
|
+
const result = {};
|
|
203
|
+
if (record["matcherAnswers"] !== undefined) {
|
|
204
|
+
result.matcherAnswers = validateRecoveredOrExit(record["matcherAnswers"], MATCHER_ANSWERS_ARRAY_SCHEMA, "answers-file:matcherAnswers", format);
|
|
205
|
+
}
|
|
206
|
+
if (record["expertiseAnswers"] !== undefined) {
|
|
207
|
+
result.expertiseAnswers = validateRecoveredOrExit(record["expertiseAnswers"], EXPERTISE_ANSWERS_ARRAY_SCHEMA, "answers-file:expertiseAnswers", format);
|
|
208
|
+
}
|
|
209
|
+
return result;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Narrow the parsed JSON from `--pitch-file` into the `pitchInput`
|
|
213
|
+
* record shape. Stage-2 (#438): the inner shape is validated against
|
|
214
|
+
* the recovered `PitchInput` Zod schema (wrapped with `.strict()` so
|
|
215
|
+
* extra unknown keys reject with a field-path error). Wrong top-level
|
|
216
|
+
* shape (array / string / null) still surfaces as a CLI-level
|
|
217
|
+
* `VALIDATION_ERROR` envelope BEFORE the recovered-schema parse runs —
|
|
218
|
+
* the JSON-object precondition is a precondition of the Zod schema's
|
|
219
|
+
* `z.object(...)` semantics.
|
|
220
|
+
*/
|
|
221
|
+
function narrowPitchPayload(payload, format) {
|
|
222
|
+
if (payload === null || typeof payload !== "object" || Array.isArray(payload)) {
|
|
223
|
+
emitErrorAndExit({
|
|
224
|
+
operation: "applications.confirm",
|
|
225
|
+
format,
|
|
226
|
+
errors: [
|
|
227
|
+
{
|
|
228
|
+
code: "VALIDATION_ERROR",
|
|
229
|
+
message: "--pitch-file: expected a JSON object matching the `PitchInput` shape.",
|
|
230
|
+
hint: "File shape: a JSON object matching the `PitchInput` schema (see `packages/core/src/__generated__/zod-schemas.ts` § PitchInputSchema).",
|
|
231
|
+
},
|
|
232
|
+
],
|
|
233
|
+
prettySummary: "applications confirm failed (VALIDATION_ERROR): --pitch-file shape is not a JSON object.",
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
return validateRecoveredOrExit(payload, STRICT_PITCH_INPUT_SCHEMA, "pitch-file", format);
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Wrapper around {@link parseAsRecovered} that maps the typed
|
|
240
|
+
* `SCHEMA_ERROR` from {@link JsonInputError} to the structured
|
|
241
|
+
* `VALIDATION_ERROR` envelope this command emits. Mirrors
|
|
242
|
+
* {@link loadJsonInputOrExit} — failure path is uniform across all
|
|
243
|
+
* input-validation errors (parse, syntax, schema).
|
|
244
|
+
*/
|
|
245
|
+
function validateRecoveredOrExit(value, schema, flagName, format) {
|
|
246
|
+
try {
|
|
247
|
+
return parseAsRecovered(value, schema, flagName);
|
|
248
|
+
}
|
|
249
|
+
catch (err) {
|
|
250
|
+
if (err instanceof JsonInputError) {
|
|
251
|
+
emitErrorAndExit({
|
|
252
|
+
operation: "applications.confirm",
|
|
253
|
+
format,
|
|
254
|
+
errors: [
|
|
255
|
+
{
|
|
256
|
+
code: "VALIDATION_ERROR",
|
|
257
|
+
message: err.message,
|
|
258
|
+
hint: hintForJsonInputCode(err.code),
|
|
259
|
+
},
|
|
260
|
+
],
|
|
261
|
+
prettySummary: `applications confirm failed (VALIDATION_ERROR): ${err.message}`,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
throw err;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
74
267
|
/**
|
|
75
268
|
* Render the post-confirm/reject AR projection as the indented entity
|
|
76
269
|
* preview inside the success-update envelope's pretty block. Shared
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"confirm.js","sourceRoot":"","sources":["../../../src/commands/applications/confirm.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,oCAAoC;AAEpC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"confirm.js","sourceRoot":"","sources":["../../../src/commands/applications/confirm.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,8 +10,12 @@ import { Command } from "commander";
|
|
|
10
10
|
* | `show <id>` | Detail view for one row |
|
|
11
11
|
* | `stats` | Per-status-group counts (5 server calls in parallel) |
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* Write operations on the application funnel are in scope per ADR-008
|
|
14
|
+
* (ttctl) — `hq/engineering/adr/ADR-008-application-funnel-write-side.md`.
|
|
15
|
+
* ADR-008 § Decision relaxes the #15 read-only non-goal and bounds the
|
|
16
|
+
* write surface: Interest Request confirm / reject (shipped in #411)
|
|
17
|
+
* and direct job application are in scope; `withdraw` / `edit`, bulk
|
|
18
|
+
* apply, and interview accept / reject remain explicitly out of scope.
|
|
15
19
|
*
|
|
16
20
|
* **Pagination (#377)**: the `list` leaf declares `--page` /
|
|
17
21
|
* `--per-page` (1-indexed positive integers; same `parsePaginationFlag`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/applications/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAgC,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/applications/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAgC,MAAM,WAAW,CAAC;AAqClE;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,wBAAwB,IAAI,OAAO,CAoRlD"}
|
|
@@ -4,7 +4,9 @@ import { Command, InvalidArgumentError, Option } from "commander";
|
|
|
4
4
|
import { applications } from "@ttctl/core";
|
|
5
5
|
import { OUTPUT_FORMATS } from "../../lib/output.js";
|
|
6
6
|
import { parsePaginationFlag } from "../../lib/pagination.js";
|
|
7
|
+
import { runApplicationsAvailabilityRequestShow } from "./availability-request.js";
|
|
7
8
|
import { runApplicationsConfirm } from "./confirm.js";
|
|
9
|
+
import { runApplicationsInterviewGuideShow, runApplicationsInterviewNotesShow, runApplicationsInterviewShow, } from "./interview.js";
|
|
8
10
|
import { runApplicationsList } from "./list.js";
|
|
9
11
|
import { runApplicationsReject } from "./reject.js";
|
|
10
12
|
import { runApplicationsRejectReasons } from "./reject-reasons.js";
|
|
@@ -34,8 +36,12 @@ function perPageOption() {
|
|
|
34
36
|
* | `show <id>` | Detail view for one row |
|
|
35
37
|
* | `stats` | Per-status-group counts (5 server calls in parallel) |
|
|
36
38
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
+
* Write operations on the application funnel are in scope per ADR-008
|
|
40
|
+
* (ttctl) — `hq/engineering/adr/ADR-008-application-funnel-write-side.md`.
|
|
41
|
+
* ADR-008 § Decision relaxes the #15 read-only non-goal and bounds the
|
|
42
|
+
* write surface: Interest Request confirm / reject (shipped in #411)
|
|
43
|
+
* and direct job application are in scope; `withdraw` / `edit`, bulk
|
|
44
|
+
* apply, and interview accept / reject remain explicitly out of scope.
|
|
39
45
|
*
|
|
40
46
|
* **Pagination (#377)**: the `list` leaf declares `--page` /
|
|
41
47
|
* `--per-page` (1-indexed positive integers; same `parsePaginationFlag`
|
|
@@ -101,15 +107,29 @@ export function buildApplicationsCommand() {
|
|
|
101
107
|
// the AvailabilityRequest id (NOT the activity-item id) — discover
|
|
102
108
|
// it via `applications show <activityId>` (look for the "Availability
|
|
103
109
|
// request: <id>" line).
|
|
110
|
+
// #428 — answers-file / pitch-file help text. ADR-008 § Decision
|
|
111
|
+
// Part 2 locks the JSON-file grammar; per the issue's AC the help
|
|
112
|
+
// must document the JSON shape (5-line example), the question-id
|
|
113
|
+
// discovery hint, and the stdin escape.
|
|
114
|
+
const ANSWERS_FILE_HELP = "JSON file (or `-` for stdin) containing matcher/expertise answers. Shape:\n" +
|
|
115
|
+
" {\n" +
|
|
116
|
+
' "matcherAnswers": [{"id": "<questionIdentifier>", "answer": "<value>"}],\n' +
|
|
117
|
+
' "expertiseAnswers": [{"questionId": "<questionIdentifier>", "other": null, "subjectId": null}]\n' +
|
|
118
|
+
" }\n" +
|
|
119
|
+
"Per the recovered SDL (#438): matcher answers carry the id at `id`; expertise answers carry it at `questionId`. " +
|
|
120
|
+
"Question identifiers come from `applications show <activityId>` output. Stdin escape: `--answers-file -`.";
|
|
121
|
+
const PITCH_FILE_HELP = "JSON file (or `-` for stdin) containing the PitchInput payload (single JSON object matching the recovered SDL shape). Stdin escape: `--pitch-file -`.";
|
|
104
122
|
cmd
|
|
105
123
|
.command("confirm")
|
|
106
|
-
.description("Confirm an Interest Request (DESTRUCTIVE — creates a JobApplication; no undo)")
|
|
124
|
+
.description("Confirm an Interest Request (DESTRUCTIVE — creates a JobApplication; no undo). See `Interest Requests` in the README for the full workflow.")
|
|
107
125
|
.argument("<id>", "AvailabilityRequest id (NOT the activity-item id)", parseIdArg)
|
|
108
126
|
.option("-m, --message <text>", "optional talent free-text accompanying the confirmation")
|
|
109
127
|
.option("--rate <decimal>", "requested hourly rate (decimal string, e.g. 80.00). Auto-filled from the AR's Fixed rate when omitted; required for FLEXIBLE / MARKETPLACE_FLEXIBLE ARs")
|
|
110
128
|
.addOption(new Option("--kind <kind>", "AR kind (auto-detected from metadata when omitted)").choices([
|
|
111
129
|
...applications.AVAILABILITY_REQUEST_KINDS,
|
|
112
130
|
]))
|
|
131
|
+
.option("--answers-file <path>", ANSWERS_FILE_HELP)
|
|
132
|
+
.option("--pitch-file <path>", PITCH_FILE_HELP)
|
|
113
133
|
.addOption(new Option("-o, --output <format>", "output format")
|
|
114
134
|
.choices(OUTPUT_FORMATS)
|
|
115
135
|
.default("pretty"))
|
|
@@ -121,6 +141,10 @@ export function buildApplicationsCommand() {
|
|
|
121
141
|
runOpts.rate = options.rate;
|
|
122
142
|
if (options.kind !== undefined)
|
|
123
143
|
runOpts.kind = options.kind;
|
|
144
|
+
if (options.answersFile !== undefined)
|
|
145
|
+
runOpts.answersFile = options.answersFile;
|
|
146
|
+
if (options.pitchFile !== undefined)
|
|
147
|
+
runOpts.pitchFile = options.pitchFile;
|
|
124
148
|
await runApplicationsConfirm(id, runOpts);
|
|
125
149
|
});
|
|
126
150
|
cmd
|
|
@@ -150,6 +174,91 @@ export function buildApplicationsCommand() {
|
|
|
150
174
|
.action(async (options) => {
|
|
151
175
|
await runApplicationsRejectReasons(options.output);
|
|
152
176
|
});
|
|
177
|
+
// #439 — Interview detail. Sibling sub-namespace `interview show <id>`
|
|
178
|
+
// for the rich `TalentInterview` projection (interviewer, scheduled
|
|
179
|
+
// slot, agenda link, prep-guide ref). The id comes from
|
|
180
|
+
// `applications show <activityId>` output (the `Interview: <id>`
|
|
181
|
+
// line). Read-only — no confirm / reject verbs (those remain
|
|
182
|
+
// out-of-scope per ADR-008 § Decision).
|
|
183
|
+
const interviewCmd = cmd
|
|
184
|
+
.command("interview")
|
|
185
|
+
.description("Interview detail (read-only). See `applications show <activityId>` for the id.");
|
|
186
|
+
interviewCmd
|
|
187
|
+
.command("show")
|
|
188
|
+
.description("Show one interview by id")
|
|
189
|
+
.argument("<id>", "id of the interview to show", parseIdArg)
|
|
190
|
+
.addOption(new Option("-o, --output <format>", "output format")
|
|
191
|
+
.choices(OUTPUT_FORMATS)
|
|
192
|
+
.default("pretty"))
|
|
193
|
+
.action(async (id, options) => {
|
|
194
|
+
await runApplicationsInterviewShow(id, options.output);
|
|
195
|
+
});
|
|
196
|
+
// #440 — Interview notes (portal-side `GetInterviewNotes`). Sub-sub-
|
|
197
|
+
// namespace `interview notes show <jobId>` for the talent's prep
|
|
198
|
+
// notes attached to an interview. Read-only.
|
|
199
|
+
//
|
|
200
|
+
// **Input is the JOB id, not the interview id** — the wire op takes
|
|
201
|
+
// `$jobId: ID!` and traverses
|
|
202
|
+
// `viewer.job(id).activityItem.interview.{id, kind, talentNotes}`.
|
|
203
|
+
// Discover the job id via `applications interview show <interviewId>`
|
|
204
|
+
// (the `Job → Job id` line) or `applications show <activityId>`.
|
|
205
|
+
const notesCmd = interviewCmd
|
|
206
|
+
.command("notes")
|
|
207
|
+
.description("Interview prep notes (read-only). Sub-sub-namespace of `interview`.");
|
|
208
|
+
notesCmd
|
|
209
|
+
.command("show")
|
|
210
|
+
.description("Read the talent's prep notes for the interview attached to a job (input is the JOB id, NOT the interview id)")
|
|
211
|
+
.argument("<jobId>", "TalentJob id (discover via `applications interview show <interviewId>` → `Job → Job id`)", parseIdArg)
|
|
212
|
+
.addOption(new Option("-o, --output <format>", "output format")
|
|
213
|
+
.choices(OUTPUT_FORMATS)
|
|
214
|
+
.default("pretty"))
|
|
215
|
+
.action(async (jobId, options) => {
|
|
216
|
+
await runApplicationsInterviewNotesShow(jobId, options.output);
|
|
217
|
+
});
|
|
218
|
+
// #470 — Interview prep guide (mobile-gateway `InterviewGuide`).
|
|
219
|
+
// Sub-sub-namespace `interview guide show <interviewId>` for the
|
|
220
|
+
// interview-prep guide content (sections + tips) attached to one
|
|
221
|
+
// interview. Read-only.
|
|
222
|
+
//
|
|
223
|
+
// Sibling of `notes` — both deepen the `interview` namespace. Input
|
|
224
|
+
// is the INTERVIEW id (the wire op takes `$interviewId: ID!`).
|
|
225
|
+
// The `Prep guide → ID: <guideId>` line surfaced by
|
|
226
|
+
// `applications interview show` is the back-pointer; the guide
|
|
227
|
+
// CONTENT lives behind this leaf.
|
|
228
|
+
const guideCmd = interviewCmd
|
|
229
|
+
.command("guide")
|
|
230
|
+
.description("Interview prep guide (sections + tips). Sub-sub-namespace of `interview`.");
|
|
231
|
+
guideCmd
|
|
232
|
+
.command("show")
|
|
233
|
+
.description("Read the interview-prep guide content (sections + tips) for one interview")
|
|
234
|
+
.argument("<interviewId>", "Interview id (discover via `applications interview show <interviewId>` or `applications show <activityId>`)", parseIdArg)
|
|
235
|
+
.addOption(new Option("-o, --output <format>", "output format")
|
|
236
|
+
.choices(OUTPUT_FORMATS)
|
|
237
|
+
.default("pretty"))
|
|
238
|
+
.action(async (interviewId, options) => {
|
|
239
|
+
await runApplicationsInterviewGuideShow(interviewId, options.output);
|
|
240
|
+
});
|
|
241
|
+
// #442 — Availability-request detail. Sibling read-only sub-namespace
|
|
242
|
+
// `availability-request show <id>` for the rich `AvailabilityRequest`
|
|
243
|
+
// projection (status, kind, recruiter-pinned Fixed rate, recruiter
|
|
244
|
+
// comment, lifecycle timestamps, job). The `<id>` is the
|
|
245
|
+
// `AvailabilityRequest.id` from `applications show <activityId>`
|
|
246
|
+
// output (the `Availability request: <id>` line) — the SAME id the
|
|
247
|
+
// `confirm` / `reject` write-side leaves accept, NOT the activity-item
|
|
248
|
+
// id. Read-only — the confirm / reject verbs are the write surface.
|
|
249
|
+
const availabilityRequestCmd = cmd
|
|
250
|
+
.command("availability-request")
|
|
251
|
+
.description("Availability-request detail (read-only). See `applications show <activityId>` for the id.");
|
|
252
|
+
availabilityRequestCmd
|
|
253
|
+
.command("show")
|
|
254
|
+
.description("Show one availability request by id")
|
|
255
|
+
.argument("<id>", "AvailabilityRequest id (NOT the activity-item id)", parseIdArg)
|
|
256
|
+
.addOption(new Option("-o, --output <format>", "output format")
|
|
257
|
+
.choices(OUTPUT_FORMATS)
|
|
258
|
+
.default("pretty"))
|
|
259
|
+
.action(async (id, options) => {
|
|
260
|
+
await runApplicationsAvailabilityRequestShow(id, options.output);
|
|
261
|
+
});
|
|
153
262
|
return cmd;
|
|
154
263
|
}
|
|
155
264
|
/**
|