@fink-andreas/pi-linear-tools 0.1.0 → 0.2.1
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/CHANGELOG.md +20 -1
- package/README.md +18 -2
- package/extensions/pi-linear-tools.js +449 -113
- package/index.js +916 -6
- package/package.json +6 -4
- package/src/auth/callback-server.js +337 -0
- package/src/auth/index.js +246 -0
- package/src/auth/oauth.js +281 -0
- package/src/auth/pkce.js +111 -0
- package/src/auth/token-refresh.js +210 -0
- package/src/auth/token-store.js +415 -0
- package/src/cli.js +238 -65
- package/src/handlers.js +18 -10
- package/src/linear-client.js +36 -6
- package/src/linear.js +16 -9
- package/src/settings.js +107 -6
package/src/linear.js
CHANGED
|
@@ -1136,14 +1136,9 @@ export async function updateProjectMilestone(client, milestoneId, patch = {}) {
|
|
|
1136
1136
|
updateInput.targetDate = patch.targetDate;
|
|
1137
1137
|
}
|
|
1138
1138
|
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
if (!validStatuses.includes(status)) {
|
|
1143
|
-
throw new Error(`Invalid status: ${status}. Valid values: ${validStatuses.join(', ')}`);
|
|
1144
|
-
}
|
|
1145
|
-
updateInput.status = status;
|
|
1146
|
-
}
|
|
1139
|
+
// Note: status is a computed/read-only field in Linear's API (ProjectMilestoneStatus enum)
|
|
1140
|
+
// It cannot be set via ProjectMilestoneUpdateInput. The status values (done, next, overdue, unstarted)
|
|
1141
|
+
// are automatically determined by Linear based on milestone progress and dates.
|
|
1147
1142
|
|
|
1148
1143
|
if (Object.keys(updateInput).length === 0) {
|
|
1149
1144
|
throw new Error('No update fields provided');
|
|
@@ -1166,14 +1161,26 @@ export async function updateProjectMilestone(client, milestoneId, patch = {}) {
|
|
|
1166
1161
|
* Delete a project milestone
|
|
1167
1162
|
* @param {LinearClient} client - Linear SDK client
|
|
1168
1163
|
* @param {string} milestoneId - Milestone ID
|
|
1169
|
-
* @returns {Promise<{success: boolean, milestoneId: string}>}
|
|
1164
|
+
* @returns {Promise<{success: boolean, milestoneId: string, name: string|null}>}
|
|
1170
1165
|
*/
|
|
1171
1166
|
export async function deleteProjectMilestone(client, milestoneId) {
|
|
1167
|
+
let milestoneName = null;
|
|
1168
|
+
|
|
1169
|
+
try {
|
|
1170
|
+
const existing = await client.projectMilestone(milestoneId);
|
|
1171
|
+
if (existing?.name) {
|
|
1172
|
+
milestoneName = existing.name;
|
|
1173
|
+
}
|
|
1174
|
+
} catch {
|
|
1175
|
+
milestoneName = null;
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1172
1178
|
const result = await client.deleteProjectMilestone(milestoneId);
|
|
1173
1179
|
|
|
1174
1180
|
return {
|
|
1175
1181
|
success: result.success,
|
|
1176
1182
|
milestoneId,
|
|
1183
|
+
name: milestoneName,
|
|
1177
1184
|
};
|
|
1178
1185
|
}
|
|
1179
1186
|
|
package/src/settings.js
CHANGED
|
@@ -10,8 +10,13 @@ import { debug, warn, error as logError } from './logger.js';
|
|
|
10
10
|
|
|
11
11
|
export function getDefaultSettings() {
|
|
12
12
|
return {
|
|
13
|
-
schemaVersion:
|
|
14
|
-
|
|
13
|
+
schemaVersion: 2,
|
|
14
|
+
authMethod: 'api-key', // 'api-key' or 'oauth'
|
|
15
|
+
apiKey: null, // Legacy API key (migrated from linearApiKey)
|
|
16
|
+
oauth: {
|
|
17
|
+
clientId: 'a3e177176c6697611367f1a2405d4a34',
|
|
18
|
+
redirectUri: 'http://localhost:34711/callback',
|
|
19
|
+
},
|
|
15
20
|
defaultTeam: null,
|
|
16
21
|
defaultWorkspace: null,
|
|
17
22
|
projects: {},
|
|
@@ -21,12 +26,70 @@ export function getDefaultSettings() {
|
|
|
21
26
|
function migrateSettings(settings) {
|
|
22
27
|
const migrated = { ...(settings || {}) };
|
|
23
28
|
|
|
29
|
+
// Set default schema version if not set
|
|
24
30
|
if (migrated.schemaVersion === undefined) {
|
|
25
31
|
migrated.schemaVersion = 1;
|
|
26
32
|
}
|
|
27
33
|
|
|
28
|
-
|
|
29
|
-
|
|
34
|
+
// Migration from schema version 1 to 2
|
|
35
|
+
if (migrated.schemaVersion === 1) {
|
|
36
|
+
debug('Migrating settings from schema version 1 to 2');
|
|
37
|
+
|
|
38
|
+
// Migrate linearApiKey to apiKey
|
|
39
|
+
if (migrated.linearApiKey !== undefined) {
|
|
40
|
+
migrated.apiKey = migrated.linearApiKey;
|
|
41
|
+
delete migrated.linearApiKey;
|
|
42
|
+
} else {
|
|
43
|
+
migrated.apiKey = null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Set default auth method
|
|
47
|
+
migrated.authMethod = 'api-key';
|
|
48
|
+
|
|
49
|
+
// Add OAuth config
|
|
50
|
+
if (!migrated.oauth || typeof migrated.oauth !== 'object') {
|
|
51
|
+
migrated.oauth = {
|
|
52
|
+
clientId: 'a3e177176c6697611367f1a2405d4a34',
|
|
53
|
+
redirectUri: 'http://localhost:34711/callback',
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Update schema version
|
|
58
|
+
migrated.schemaVersion = 2;
|
|
59
|
+
|
|
60
|
+
debug('Settings migration to version 2 complete');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Ensure authMethod is set
|
|
64
|
+
if (migrated.authMethod === undefined) {
|
|
65
|
+
migrated.authMethod = 'api-key';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Ensure apiKey is set (for backward compatibility)
|
|
69
|
+
if (migrated.apiKey === undefined) {
|
|
70
|
+
migrated.apiKey = null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Ensure oauth config exists
|
|
74
|
+
if (!migrated.oauth || typeof migrated.oauth !== 'object') {
|
|
75
|
+
migrated.oauth = {
|
|
76
|
+
clientId: 'a3e177176c6697611367f1a2405d4a34',
|
|
77
|
+
redirectUri: 'http://localhost:34711/callback',
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Ensure oauth has clientId and redirectUri
|
|
82
|
+
if (migrated.oauth.clientId === undefined) {
|
|
83
|
+
migrated.oauth.clientId = 'a3e177176c6697611367f1a2405d4a34';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (migrated.oauth.redirectUri === undefined) {
|
|
87
|
+
migrated.oauth.redirectUri = 'http://localhost:34711/callback';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Legacy: ensure linearApiKey is null (for validation)
|
|
91
|
+
if (migrated.linearApiKey !== undefined) {
|
|
92
|
+
delete migrated.linearApiKey;
|
|
30
93
|
}
|
|
31
94
|
|
|
32
95
|
if (migrated.defaultTeam === undefined) {
|
|
@@ -37,10 +100,17 @@ function migrateSettings(settings) {
|
|
|
37
100
|
migrated.defaultWorkspace = null;
|
|
38
101
|
}
|
|
39
102
|
|
|
103
|
+
// Remove deprecated debug_reload field
|
|
104
|
+
if (Object.prototype.hasOwnProperty.call(migrated, 'debug_reload')) {
|
|
105
|
+
delete migrated.debug_reload;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Ensure projects is an object
|
|
40
109
|
if (!migrated.projects || typeof migrated.projects !== 'object' || Array.isArray(migrated.projects)) {
|
|
41
110
|
migrated.projects = {};
|
|
42
111
|
}
|
|
43
112
|
|
|
113
|
+
// Migrate project scopes
|
|
44
114
|
for (const [projectId, cfg] of Object.entries(migrated.projects)) {
|
|
45
115
|
if (!cfg || typeof cfg !== 'object' || Array.isArray(cfg)) {
|
|
46
116
|
migrated.projects[projectId] = { scope: { team: null } };
|
|
@@ -71,8 +141,39 @@ export function validateSettings(settings) {
|
|
|
71
141
|
errors.push('settings.schemaVersion must be a positive number');
|
|
72
142
|
}
|
|
73
143
|
|
|
74
|
-
|
|
75
|
-
|
|
144
|
+
// Validate authMethod
|
|
145
|
+
if (settings.authMethod !== undefined && settings.authMethod !== null) {
|
|
146
|
+
if (typeof settings.authMethod !== 'string') {
|
|
147
|
+
errors.push('settings.authMethod must be a string');
|
|
148
|
+
} else if (
|
|
149
|
+
!['api-key', 'oauth'].includes(settings.authMethod)
|
|
150
|
+
) {
|
|
151
|
+
errors.push('settings.authMethod must be "api-key" or "oauth"');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Validate apiKey (for backward compatibility)
|
|
156
|
+
if (settings.apiKey !== null && settings.apiKey !== undefined && typeof settings.apiKey !== 'string') {
|
|
157
|
+
errors.push('settings.apiKey must be a string or null');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Reject legacy linearApiKey field
|
|
161
|
+
if (settings.linearApiKey !== undefined) {
|
|
162
|
+
errors.push('settings.linearApiKey is deprecated. Use settings.apiKey instead.');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Validate oauth config
|
|
166
|
+
if (settings.oauth !== undefined && settings.oauth !== null) {
|
|
167
|
+
if (typeof settings.oauth !== 'object' || Array.isArray(settings.oauth)) {
|
|
168
|
+
errors.push('settings.oauth must be an object');
|
|
169
|
+
} else {
|
|
170
|
+
if (settings.oauth.clientId !== undefined && typeof settings.oauth.clientId !== 'string') {
|
|
171
|
+
errors.push('settings.oauth.clientId must be a string');
|
|
172
|
+
}
|
|
173
|
+
if (settings.oauth.redirectUri !== undefined && typeof settings.oauth.redirectUri !== 'string') {
|
|
174
|
+
errors.push('settings.oauth.redirectUri must be a string');
|
|
175
|
+
}
|
|
176
|
+
}
|
|
76
177
|
}
|
|
77
178
|
|
|
78
179
|
if (settings.defaultTeam !== null && settings.defaultTeam !== undefined && typeof settings.defaultTeam !== 'string') {
|