@enrichlayer/el-linear 1.10.0 → 1.15.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/README.md +126 -10
- package/claude-skills/linear-operations/SKILL.md +41 -1
- package/dist/auth/linear-credential.d.ts +27 -0
- package/dist/auth/linear-credential.js +1 -0
- package/dist/auth/oauth-app-config.d.ts +4 -3
- package/dist/auth/oauth-app-config.js +13 -2
- package/dist/auth/oauth-callback.d.ts +2 -3
- package/dist/auth/oauth-callback.js +2 -2
- package/dist/auth/oauth-client.d.ts +8 -2
- package/dist/auth/oauth-client.js +26 -0
- package/dist/auth/oauth-fs.d.ts +2 -1
- package/dist/auth/oauth-headless.d.ts +2 -1
- package/dist/auth/oauth-storage.d.ts +5 -1
- package/dist/auth/oauth-storage.js +1 -1
- package/dist/auth/oauth-token.d.ts +4 -3
- package/dist/auth/oauth-token.js +16 -4
- package/dist/auth/token-resolver.d.ts +14 -5
- package/dist/auth/token-resolver.js +6 -1
- package/dist/commands/batch.js +18 -21
- package/dist/commands/comments.js +5 -5
- package/dist/commands/config.js +178 -5
- package/dist/commands/init/aliases.js +1 -1
- package/dist/commands/init/defaults.d.ts +2 -1
- package/dist/commands/init/index.js +45 -35
- package/dist/commands/init/oauth.d.ts +4 -1
- package/dist/commands/init/oauth.js +22 -4
- package/dist/commands/init/shared.d.ts +24 -2
- package/dist/commands/init/shared.js +35 -4
- package/dist/commands/init/token.d.ts +3 -3
- package/dist/commands/init/token.js +5 -24
- package/dist/commands/init/workspace.d.ts +2 -1
- package/dist/commands/init/workspace.js +1 -1
- package/dist/commands/introspect.d.ts +27 -0
- package/dist/commands/introspect.js +178 -0
- package/dist/commands/issues/branch.js +9 -1
- package/dist/commands/issues/relations.d.ts +3 -14
- package/dist/commands/issues/relations.js +3 -3
- package/dist/commands/issues.js +222 -43
- package/dist/commands/labels.js +2 -1
- package/dist/commands/profile.js +1 -0
- package/dist/commands/projects.d.ts +2 -0
- package/dist/commands/projects.js +91 -7
- package/dist/commands/read-shortcut.d.ts +1 -1
- package/dist/commands/read-shortcut.js +28 -8
- package/dist/commands/refs.js +67 -8
- package/dist/commands/search.js +30 -5
- package/dist/commands/users.js +4 -2
- package/dist/config/config.d.ts +99 -1
- package/dist/config/config.js +264 -52
- package/dist/config/error-enrichment.d.ts +62 -0
- package/dist/config/error-enrichment.js +417 -0
- package/dist/config/issue-validation.d.ts +37 -0
- package/dist/config/issue-validation.js +63 -1
- package/dist/config/paths.d.ts +2 -8
- package/dist/config/paths.js +4 -2
- package/dist/config/resolver.d.ts +8 -1
- package/dist/config/resolver.js +9 -2
- package/dist/main.js +13 -1
- package/dist/queries/comments-types.d.ts +15 -9
- package/dist/queries/common.d.ts +2 -2
- package/dist/queries/common.js +8 -0
- package/dist/queries/documents-types.d.ts +4 -3
- package/dist/queries/introspect-types.d.ts +8 -7
- package/dist/queries/issues-types.d.ts +92 -27
- package/dist/queries/issues.d.ts +49 -10
- package/dist/queries/issues.js +125 -5
- package/dist/queries/labels-types.d.ts +7 -6
- package/dist/queries/project-milestones-types.d.ts +5 -4
- package/dist/queries/project-milestones.d.ts +1 -1
- package/dist/queries/projects-types.d.ts +8 -7
- package/dist/queries/releases-types.d.ts +5 -4
- package/dist/queries/search-types.d.ts +28 -12
- package/dist/queries/templates-types.d.ts +3 -2
- package/dist/types/linear.d.ts +13 -1
- package/dist/utils/auto-link-references.d.ts +3 -3
- package/dist/utils/auto-link-references.js +1 -10
- package/dist/utils/extract-field.d.ts +19 -0
- package/dist/utils/extract-field.js +99 -0
- package/dist/utils/file-service.d.ts +6 -13
- package/dist/utils/file-service.js +0 -2
- package/dist/utils/formatters/summary.js +6 -1
- package/dist/utils/graphql-issues-service.d.ts +101 -45
- package/dist/utils/graphql-issues-service.js +252 -39
- package/dist/utils/graphql-service.d.ts +10 -12
- package/dist/utils/graphql-service.js +0 -3
- package/dist/utils/issue-reference-extractor.d.ts +7 -0
- package/dist/utils/issue-reference-extractor.js +5 -3
- package/dist/utils/issues-service-bootstrap.d.ts +28 -0
- package/dist/utils/issues-service-bootstrap.js +27 -0
- package/dist/utils/linear-service.d.ts +21 -14
- package/dist/utils/linear-service.js +73 -11
- package/dist/utils/markdown-prosemirror.js +12 -12
- package/dist/utils/mention-resolver.js +1 -1
- package/dist/utils/output.d.ts +81 -3
- package/dist/utils/output.js +61 -6
- package/dist/utils/project-slug.d.ts +21 -0
- package/dist/utils/project-slug.js +45 -0
- package/dist/utils/protected-ranges.d.ts +14 -0
- package/dist/utils/protected-ranges.js +88 -2
- package/dist/utils/sanitize-for-log.d.ts +24 -0
- package/dist/utils/sanitize-for-log.js +38 -0
- package/dist/utils/table-formatter.js +24 -0
- package/dist/utils/validators.d.ts +7 -2
- package/dist/utils/validators.js +6 -0
- package/dist/utils/workspace-url.js +20 -4
- package/package.json +2 -2
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strip anything that looks like a Linear API/OAuth token from a string.
|
|
3
|
+
*
|
|
4
|
+
* Defense in depth: today the @linear/sdk error message embeds `{ query,
|
|
5
|
+
* variables }` but not the Authorization header. A future SDK upgrade that
|
|
6
|
+
* includes headers (which upstream graphql-request has done historically)
|
|
7
|
+
* would otherwise silently write `Bearer lin_api_…` into stdout, shell
|
|
8
|
+
* history, or CI logs. The regex also catches token shapes that may show
|
|
9
|
+
* up in custom error wrappers — e.g. a network proxy that echoes the
|
|
10
|
+
* Bearer header in its 502 body.
|
|
11
|
+
*
|
|
12
|
+
* Originally lived in `commands/init/token.ts` for the wizard's error
|
|
13
|
+
* formatting. Hoisted to `utils/` so the central error path
|
|
14
|
+
* (`output.ts`'s `outputError`) can use it too — that path runs on
|
|
15
|
+
* every non-wizard CLI invocation. `init/token.ts` re-exports the
|
|
16
|
+
* symbol so existing imports under `init/` keep working.
|
|
17
|
+
*
|
|
18
|
+
* The OAuth token-exchange / refresh / revoke paths also call this
|
|
19
|
+
* function at source (`auth/oauth-token.ts`'s `postForm` + `revokeToken`,
|
|
20
|
+
* `auth/token-resolver.ts`'s refresh-failure rewrap) so a future caller
|
|
21
|
+
* that catches+rethrows or logs mid-chain can't leak a token before the
|
|
22
|
+
* error reaches `outputError`. Defense in depth (DEV-4065).
|
|
23
|
+
*/
|
|
24
|
+
// Personal-API tokens (`lin_api_…`) and OAuth access/refresh tokens
|
|
25
|
+
// (`lin_oauth_…`).
|
|
26
|
+
const TOKEN_PREFIX_RE = /lin_(api|oauth)_[A-Za-z0-9_-]{16,}/g;
|
|
27
|
+
// High-entropy bearer payload fallback: catches generic Bearer-style
|
|
28
|
+
// strings adjacent to Authorization / Bearer keywords. Useful for
|
|
29
|
+
// future SDK error wrappers that might leak headers without the
|
|
30
|
+
// `lin_` prefix.
|
|
31
|
+
const BEARER_PAYLOAD_RE = /(\b(?:Authorization|Bearer)\b[:\s]*)([A-Za-z0-9_\-/+=]{40,})/gi;
|
|
32
|
+
export function sanitizeForLog(text) {
|
|
33
|
+
return text
|
|
34
|
+
.replace(TOKEN_PREFIX_RE, (m) => m.startsWith("lin_oauth_")
|
|
35
|
+
? "lin_oauth_***REDACTED***"
|
|
36
|
+
: "lin_api_***REDACTED***")
|
|
37
|
+
.replace(BEARER_PAYLOAD_RE, "$1***REDACTED***");
|
|
38
|
+
}
|
|
@@ -86,6 +86,19 @@ const ALL_COLUMNS = {
|
|
|
86
86
|
return atIdx > 0 ? name.slice(0, atIdx) : name;
|
|
87
87
|
},
|
|
88
88
|
},
|
|
89
|
+
delegate: {
|
|
90
|
+
key: "delegate",
|
|
91
|
+
header: "Delegate",
|
|
92
|
+
width: 20,
|
|
93
|
+
extract: (i) => {
|
|
94
|
+
if (!i.delegate) {
|
|
95
|
+
return "—";
|
|
96
|
+
}
|
|
97
|
+
const name = i.delegate.name;
|
|
98
|
+
const atIdx = name.indexOf("@");
|
|
99
|
+
return atIdx > 0 ? name.slice(0, atIdx) : name;
|
|
100
|
+
},
|
|
101
|
+
},
|
|
89
102
|
project: {
|
|
90
103
|
key: "project",
|
|
91
104
|
header: "Project",
|
|
@@ -229,6 +242,17 @@ const MD_COLUMNS = {
|
|
|
229
242
|
return atIdx > 0 ? name.slice(0, atIdx) : name;
|
|
230
243
|
},
|
|
231
244
|
},
|
|
245
|
+
delegate: {
|
|
246
|
+
header: "Delegate",
|
|
247
|
+
extract: (i) => {
|
|
248
|
+
if (!i.delegate) {
|
|
249
|
+
return "—";
|
|
250
|
+
}
|
|
251
|
+
const name = i.delegate.name;
|
|
252
|
+
const atIdx = name.indexOf("@");
|
|
253
|
+
return atIdx > 0 ? name.slice(0, atIdx) : name;
|
|
254
|
+
},
|
|
255
|
+
},
|
|
232
256
|
project: {
|
|
233
257
|
header: "Project",
|
|
234
258
|
extract: (i) => i.project?.name ?? "—",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { LinearPriority } from "../types/linear.js";
|
|
1
2
|
export declare function parsePositiveInt(value: string, flagName: string): number;
|
|
2
3
|
/**
|
|
3
4
|
* Parse a single priority value (for `issues create --priority`, `issues update --priority`).
|
|
@@ -5,8 +6,12 @@ export declare function parsePositiveInt(value: string, flagName: string): numbe
|
|
|
5
6
|
* Accepts:
|
|
6
7
|
* - keywords: none | urgent | high | medium | normal | low
|
|
7
8
|
* - numbers: 0 (no priority), 1 (urgent), 2 (high), 3 (medium), 4 (low)
|
|
9
|
+
*
|
|
10
|
+
* Narrows arbitrary string input to the `LinearPriority` literal union so
|
|
11
|
+
* downstream call sites get type-level guarantees that the value is in
|
|
12
|
+
* range — no `as 0 | 1 | 2 | 3 | 4` cast at the boundary.
|
|
8
13
|
*/
|
|
9
|
-
export declare function validatePriority(value: string):
|
|
14
|
+
export declare function validatePriority(value: string): LinearPriority;
|
|
10
15
|
export declare function validateHexColor(value: string): string;
|
|
11
16
|
export declare function validateIsoDate(value: string): string;
|
|
12
17
|
/**
|
|
@@ -18,5 +23,5 @@ export declare function validateIsoDate(value: string): string;
|
|
|
18
23
|
* resolve to `[]`.
|
|
19
24
|
*/
|
|
20
25
|
export declare function splitList(value: string | undefined | null | false): string[];
|
|
21
|
-
export declare function parsePriorityFilter(value: string):
|
|
26
|
+
export declare function parsePriorityFilter(value: string): LinearPriority[];
|
|
22
27
|
export declare const PRIORITY_LABELS: Record<number, string>;
|
package/dist/utils/validators.js
CHANGED
|
@@ -14,6 +14,10 @@ export function parsePositiveInt(value, flagName) {
|
|
|
14
14
|
* Accepts:
|
|
15
15
|
* - keywords: none | urgent | high | medium | normal | low
|
|
16
16
|
* - numbers: 0 (no priority), 1 (urgent), 2 (high), 3 (medium), 4 (low)
|
|
17
|
+
*
|
|
18
|
+
* Narrows arbitrary string input to the `LinearPriority` literal union so
|
|
19
|
+
* downstream call sites get type-level guarantees that the value is in
|
|
20
|
+
* range — no `as 0 | 1 | 2 | 3 | 4` cast at the boundary.
|
|
17
21
|
*/
|
|
18
22
|
export function validatePriority(value) {
|
|
19
23
|
const asName = PRIORITY_NAMES[value.toLowerCase()];
|
|
@@ -24,6 +28,8 @@ export function validatePriority(value) {
|
|
|
24
28
|
if (Number.isNaN(n) || n < 0 || n > 4) {
|
|
25
29
|
throw invalidParameterError("--priority", `"${value}" is not valid. Use names (none, urgent, high, medium/normal, low) or numbers (0-4).`);
|
|
26
30
|
}
|
|
31
|
+
// The 0-4 range check above is what narrows `n` to LinearPriority;
|
|
32
|
+
// TypeScript can't infer that automatically.
|
|
27
33
|
return n;
|
|
28
34
|
}
|
|
29
35
|
export function validateHexColor(value) {
|
|
@@ -29,18 +29,33 @@ const VIEWER_ORG_URL_KEY_QUERY = /* GraphQL */ `
|
|
|
29
29
|
}
|
|
30
30
|
`;
|
|
31
31
|
const WORKSPACE_URL_KEY_ENV = "EL_LINEAR_WORKSPACE_URL_KEY";
|
|
32
|
+
// Linear workspace URL keys are URL-safe slugs. The Linear API itself
|
|
33
|
+
// validates this shape (`viewerIsValid` in init/token.ts uses the same
|
|
34
|
+
// regex). Applying the check on env + config reads closes the gap where
|
|
35
|
+
// a malformed key (`EL_LINEAR_WORKSPACE_URL_KEY=" javascript:alert(1)#"`)
|
|
36
|
+
// would otherwise flow into markdown link URLs verbatim. Live-API path
|
|
37
|
+
// is already trusted because the value comes from `viewer.organization`.
|
|
38
|
+
// DEV-4067.
|
|
39
|
+
const VALID_URL_KEY_RE = /^[a-z0-9-]+$/i;
|
|
40
|
+
function validateUrlKey(value, source) {
|
|
41
|
+
if (!VALID_URL_KEY_RE.test(value)) {
|
|
42
|
+
throw new Error(`Invalid Linear workspace URL key from ${source}: ${JSON.stringify(value)}. ` +
|
|
43
|
+
"Must match /^[a-z0-9-]+$/i (e.g. 'verticalint').");
|
|
44
|
+
}
|
|
45
|
+
return value;
|
|
46
|
+
}
|
|
32
47
|
let cachedUrlKey;
|
|
33
48
|
export async function getWorkspaceUrlKey(graphQLService, options = {}) {
|
|
34
49
|
// 1. Per-invocation override — highest priority.
|
|
35
50
|
if (options.override) {
|
|
36
|
-
return options.override;
|
|
51
|
+
return validateUrlKey(options.override, "--workspace-url-key flag");
|
|
37
52
|
}
|
|
38
53
|
// 2. Env var. Read on every call (cheap; lets tests/CI flip the value
|
|
39
54
|
// mid-process without restarting). Not cached, so a test that sets
|
|
40
55
|
// EL_LINEAR_WORKSPACE_URL_KEY then unsets it sees the unset state.
|
|
41
56
|
const envKey = process.env[WORKSPACE_URL_KEY_ENV];
|
|
42
57
|
if (envKey) {
|
|
43
|
-
return envKey;
|
|
58
|
+
return validateUrlKey(envKey, `${WORKSPACE_URL_KEY_ENV} env var`);
|
|
44
59
|
}
|
|
45
60
|
// Layers 3-4 are cached in-process — config and the live lookup are
|
|
46
61
|
// both stable for the CLI invocation's lifetime.
|
|
@@ -50,8 +65,9 @@ export async function getWorkspaceUrlKey(graphQLService, options = {}) {
|
|
|
50
65
|
// 3. Config override.
|
|
51
66
|
const { workspaceUrlKey } = loadConfig();
|
|
52
67
|
if (workspaceUrlKey) {
|
|
53
|
-
|
|
54
|
-
|
|
68
|
+
const validated = validateUrlKey(workspaceUrlKey, "config.workspaceUrlKey");
|
|
69
|
+
cachedUrlKey = validated;
|
|
70
|
+
return validated;
|
|
55
71
|
}
|
|
56
72
|
// 4. Live API lookup — requires a GraphQL service.
|
|
57
73
|
if (!graphQLService) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@enrichlayer/el-linear",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.0",
|
|
4
4
|
"description": "A pragmatic CLI for Linear.app — deterministic team/label/member resolution, structured issue validation, configurable term enforcement, and a GraphQL escape hatch.",
|
|
5
5
|
"main": "dist/main.js",
|
|
6
6
|
"types": "dist/main.d.ts",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@inquirer/prompts": "^8.4.2",
|
|
50
|
-
"@linear/sdk": "^
|
|
50
|
+
"@linear/sdk": "^84.0.0",
|
|
51
51
|
"commander": "^14.0.0",
|
|
52
52
|
"picocolors": "^1.1.1"
|
|
53
53
|
},
|