@gaosh3n/pi-package-manager 0.1.3 → 0.1.4

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/index.ts CHANGED
@@ -49,6 +49,7 @@ export default function initPackageManager(
49
49
  )
50
50
 
51
51
  pi.on("session_start", controller.onSessionStart)
52
+ pi.on("session_shutdown", controller.onSessionShutdown)
52
53
 
53
54
  pi.registerCommand("package-manager", {
54
55
  description:
@@ -29,8 +29,11 @@ export function createPackageManagerController(
29
29
  pi: Pick<ExtensionAPI, "appendEntry" | "sendUserMessage" | "exec">,
30
30
  deps: PackageManagerDeps = defaultPackageManagerDeps,
31
31
  ) {
32
+ let sessionIsActive = true
33
+
32
34
  return {
33
35
  onSessionStart,
36
+ onSessionShutdown,
34
37
  handleStatus,
35
38
  handleUpdate,
36
39
  handleInstall,
@@ -41,6 +44,8 @@ export function createPackageManagerController(
41
44
  event: Pick<SessionStartEvent, "reason">,
42
45
  _ctx: ExtensionContext,
43
46
  ): Promise<void> {
47
+ sessionIsActive = true
48
+
44
49
  if (!shouldAutoUpdateOnSessionStart(event)) {
45
50
  return
46
51
  }
@@ -50,6 +55,10 @@ export function createPackageManagerController(
50
55
  })
51
56
  }
52
57
 
58
+ async function onSessionShutdown(): Promise<void> {
59
+ sessionIsActive = false
60
+ }
61
+
53
62
  async function handleStatus(ctx: ExtensionCommandContext): Promise<void> {
54
63
  const lastAutoUpdate = getLastAutoUpdateRecord(ctx.sessionManager.getEntries())
55
64
 
@@ -57,26 +66,37 @@ export function createPackageManagerController(
57
66
 
58
67
  try {
59
68
  const availableUpdates = await deps.checkForAvailableUpdates(ctx)
60
- pi.appendEntry(
61
- REPORT_ENTRY_TYPE,
62
- createStatusReport({
69
+
70
+ if (!isSessionActive()) {
71
+ return
72
+ }
73
+
74
+ appendReport({
75
+ type: REPORT_ENTRY_TYPE,
76
+ report: createStatusReport({
63
77
  availableUpdates,
64
78
  lastAutoUpdate,
65
79
  }),
66
- )
80
+ })
67
81
  } catch (error) {
68
- pi.appendEntry(
69
- REPORT_ENTRY_TYPE,
70
- createStatusErrorReport(
82
+ if (!isSessionActive()) {
83
+ return
84
+ }
85
+
86
+ appendReport({
87
+ type: REPORT_ENTRY_TYPE,
88
+ report: createStatusErrorReport(
71
89
  {
72
90
  availableUpdates: [],
73
91
  lastAutoUpdate,
74
92
  },
75
93
  getErrorMessage(error),
76
94
  ),
77
- )
95
+ })
78
96
  } finally {
79
- clearPackageManagerWidget(ctx)
97
+ if (isSessionActive()) {
98
+ clearPackageManagerWidget(ctx)
99
+ }
80
100
  }
81
101
  }
82
102
 
@@ -97,6 +117,10 @@ export function createPackageManagerController(
97
117
 
98
118
  const availableUpdates = await deps.checkForAvailableUpdates(ctx)
99
119
 
120
+ if (!isSessionActive()) {
121
+ return
122
+ }
123
+
100
124
  if (availableUpdates.length === 0) {
101
125
  appendSkippedResult(startedAtUtc, "No package updates are available.")
102
126
  return
@@ -108,6 +132,11 @@ export function createPackageManagerController(
108
132
  })
109
133
 
110
134
  const result = await deps.runNativeUpdate(pi, ctx)
135
+
136
+ if (!isSessionActive()) {
137
+ return
138
+ }
139
+
111
140
  const output = getExecDisplayOutput(result)
112
141
 
113
142
  if (result.code === 0) {
@@ -117,17 +146,18 @@ export function createPackageManagerController(
117
146
  outcome: "succeeded",
118
147
  packagesUpdated: availableUpdates.length,
119
148
  })
120
-
121
- appendAutoUpdateRecordAndReport(
122
- pi,
149
+ const report = createAutoUpdateResultReport({
123
150
  record,
124
- createAutoUpdateResultReport({
125
- record,
126
- output,
127
- reloadAfterSeconds: RELOAD_COUNTDOWN_SECONDS,
128
- }),
129
- )
130
- await runReloadCountdown(ctx)
151
+ output,
152
+ reloadAfterSeconds: RELOAD_COUNTDOWN_SECONDS,
153
+ })
154
+
155
+ appendAutoUpdateRecordAndReport(pi, record, report)
156
+
157
+ if (!(await runReloadCountdown(ctx))) {
158
+ return
159
+ }
160
+
131
161
  clearPackageManagerWidget(ctx)
132
162
  shouldClearWidget = false
133
163
  await ctx.reload()
@@ -141,14 +171,15 @@ export function createPackageManagerController(
141
171
  packagesUpdated: 0,
142
172
  reason: getExecFailureDetail(result, "Package update command failed."),
143
173
  })
174
+ const report = createAutoUpdateResultReport({ record, output })
144
175
 
145
- appendAutoUpdateRecordAndReport(
146
- pi,
147
- record,
148
- createAutoUpdateResultReport({ record, output }),
149
- )
176
+ appendAutoUpdateRecordAndReport(pi, record, report)
150
177
  notifyStartupFailure(ctx, options.startupTriggered)
151
178
  } catch (error) {
179
+ if (!isSessionActive()) {
180
+ return
181
+ }
182
+
152
183
  const record = createAutoUpdateRecord({
153
184
  startedAtUtc,
154
185
  endedAtUtc: deps.nowIso(),
@@ -156,15 +187,12 @@ export function createPackageManagerController(
156
187
  packagesUpdated: 0,
157
188
  reason: getErrorMessage(error),
158
189
  })
190
+ const report = createAutoUpdateResultReport({ record })
159
191
 
160
- appendAutoUpdateRecordAndReport(
161
- pi,
162
- record,
163
- createAutoUpdateResultReport({ record }),
164
- )
192
+ appendAutoUpdateRecordAndReport(pi, record, report)
165
193
  notifyStartupFailure(ctx, options.startupTriggered)
166
194
  } finally {
167
- if (shouldClearWidget) {
195
+ if (shouldClearWidget && isSessionActive()) {
168
196
  clearPackageManagerWidget(ctx)
169
197
  }
170
198
  }
@@ -186,7 +214,7 @@ export function createPackageManagerController(
186
214
  )
187
215
  )?.trim()
188
216
 
189
- if (source === undefined) {
217
+ if (!isSessionActive() || source === undefined) {
190
218
  return
191
219
  }
192
220
 
@@ -200,49 +228,47 @@ export function createPackageManagerController(
200
228
 
201
229
  try {
202
230
  const result = await deps.runNativeInstall(pi, ctx, source)
203
- const output = getExecDisplayOutput(result)
204
231
 
205
- if (result.code === 0) {
206
- pi.appendEntry(
207
- REPORT_ENTRY_TYPE,
208
- createInstallResultReport({
209
- startedAtUtc,
210
- endedAtUtc: deps.nowIso(),
211
- source,
212
- outcome: "succeeded",
213
- output,
214
- }),
215
- )
232
+ if (!isSessionActive()) {
216
233
  return
217
234
  }
218
235
 
219
- pi.appendEntry(
220
- REPORT_ENTRY_TYPE,
221
- createInstallResultReport({
222
- startedAtUtc,
223
- endedAtUtc: deps.nowIso(),
224
- source,
225
- outcome: "failed",
226
- output,
227
- reason: getExecFailureDetail(
228
- result,
229
- "Package install command failed.",
230
- ),
231
- }),
232
- )
236
+ const output = getExecDisplayOutput(result)
237
+ const report = createInstallResultReport({
238
+ startedAtUtc,
239
+ endedAtUtc: deps.nowIso(),
240
+ source,
241
+ outcome: result.code === 0 ? "succeeded" : "failed",
242
+ output,
243
+ reason:
244
+ result.code === 0
245
+ ? undefined
246
+ : getExecFailureDetail(
247
+ result,
248
+ "Package install command failed.",
249
+ ),
250
+ })
251
+
252
+ appendReport({ type: REPORT_ENTRY_TYPE, report })
233
253
  } catch (error) {
234
- pi.appendEntry(
235
- REPORT_ENTRY_TYPE,
236
- createInstallResultReport({
254
+ if (!isSessionActive()) {
255
+ return
256
+ }
257
+
258
+ appendReport({
259
+ type: REPORT_ENTRY_TYPE,
260
+ report: createInstallResultReport({
237
261
  startedAtUtc,
238
262
  endedAtUtc: deps.nowIso(),
239
263
  source,
240
264
  outcome: "failed",
241
265
  reason: getErrorMessage(error),
242
266
  }),
243
- )
267
+ })
244
268
  } finally {
245
- clearPackageManagerWidget(ctx)
269
+ if (isSessionActive()) {
270
+ clearPackageManagerWidget(ctx)
271
+ }
246
272
  }
247
273
  }
248
274
 
@@ -254,6 +280,10 @@ export function createPackageManagerController(
254
280
 
255
281
  const packages = await deps.listConfiguredPackages(ctx)
256
282
 
283
+ if (!isSessionActive()) {
284
+ return
285
+ }
286
+
257
287
  if (packages.length === 0) {
258
288
  ctx.ui.notify("No Pi packages are available to uninstall.", "info")
259
289
  return
@@ -261,7 +291,7 @@ export function createPackageManagerController(
261
291
 
262
292
  const selectedSources = await promptForPackagesToUninstall(ctx, packages)
263
293
 
264
- if (selectedSources === undefined) {
294
+ if (!isSessionActive() || selectedSources === undefined) {
265
295
  return
266
296
  }
267
297
 
@@ -295,6 +325,11 @@ export function createPackageManagerController(
295
325
  })
296
326
 
297
327
  const result = await deps.runNativeUninstall(pi, ctx, source)
328
+
329
+ if (!isSessionActive()) {
330
+ return
331
+ }
332
+
298
333
  const output = getExecDisplayOutput(result)
299
334
 
300
335
  if (result.code === 0) {
@@ -311,9 +346,9 @@ export function createPackageManagerController(
311
346
  )
312
347
  }
313
348
 
314
- pi.appendEntry(
315
- REPORT_ENTRY_TYPE,
316
- createUninstallResultReport({
349
+ appendReport({
350
+ type: REPORT_ENTRY_TYPE,
351
+ report: createUninstallResultReport({
317
352
  startedAtUtc,
318
353
  endedAtUtc: deps.nowIso(),
319
354
  sources,
@@ -334,8 +369,12 @@ export function createPackageManagerController(
334
369
  ? `Failed to uninstall ${failedSources[0]}.`
335
370
  : undefined,
336
371
  }),
337
- )
372
+ })
338
373
  } catch (error) {
374
+ if (!isSessionActive()) {
375
+ return
376
+ }
377
+
339
378
  const failedSource = sources[succeededSources.length]
340
379
  const errorMessage = getErrorMessage(error)
341
380
 
@@ -346,9 +385,9 @@ export function createPackageManagerController(
346
385
  outputSections.push(
347
386
  failedSource ? `[${failedSource}]\n${errorMessage}` : errorMessage,
348
387
  )
349
- pi.appendEntry(
350
- REPORT_ENTRY_TYPE,
351
- createUninstallResultReport({
388
+ appendReport({
389
+ type: REPORT_ENTRY_TYPE,
390
+ report: createUninstallResultReport({
352
391
  startedAtUtc,
353
392
  endedAtUtc: deps.nowIso(),
354
393
  sources,
@@ -358,9 +397,11 @@ export function createPackageManagerController(
358
397
  output: outputSections.join("\n\n"),
359
398
  reason: errorMessage,
360
399
  }),
361
- )
400
+ })
362
401
  } finally {
363
- clearPackageManagerWidget(ctx)
402
+ if (isSessionActive()) {
403
+ clearPackageManagerWidget(ctx)
404
+ }
364
405
  }
365
406
  }
366
407
 
@@ -372,26 +413,44 @@ export function createPackageManagerController(
372
413
  packagesUpdated: 0,
373
414
  reason,
374
415
  })
416
+ const report = createAutoUpdateResultReport({ record })
375
417
 
376
- appendAutoUpdateRecordAndReport(
377
- pi,
378
- record,
379
- createAutoUpdateResultReport({ record }),
380
- )
418
+ appendAutoUpdateRecordAndReport(pi, record, report)
419
+ }
420
+
421
+ function appendReport(entry: {
422
+ type: string
423
+ report: ReturnType<typeof createStatusReport>
424
+ }): void {
425
+ pi.appendEntry(entry.type, entry.report)
426
+ }
427
+
428
+ function isSessionActive(): boolean {
429
+ return sessionIsActive
381
430
  }
382
431
 
383
432
  async function runReloadCountdown(
384
433
  ctx: ExtensionContext,
385
434
  seconds = RELOAD_COUNTDOWN_SECONDS,
386
- ): Promise<void> {
435
+ ): Promise<boolean> {
387
436
  for (let remaining = seconds; remaining >= 1; remaining--) {
437
+ if (!isSessionActive()) {
438
+ return false
439
+ }
440
+
388
441
  setPackageManagerWidget(ctx, {
389
442
  mode: "countdown",
390
443
  secondsRemaining: remaining,
391
444
  })
392
445
 
393
446
  await deps.sleep(1000)
447
+
448
+ if (!isSessionActive()) {
449
+ return false
450
+ }
394
451
  }
452
+
453
+ return true
395
454
  }
396
455
  }
397
456
 
@@ -36,81 +36,7 @@ export function createReportEntryRenderer() {
36
36
  return undefined
37
37
  }
38
38
 
39
- const toneColor =
40
- report.tone === "error"
41
- ? "error"
42
- : report.tone === "warning"
43
- ? "warning"
44
- : report.tone === "success"
45
- ? "success"
46
- : "accent"
47
- const lineTextTone = report.lineTone === "dim" ? "dim" : "customMessageText"
48
- const outputTextTone = report.outputTone === "dim" ? "dim" : lineTextTone
49
-
50
- const box = new Box(1, 1, (text: string) => theme.bg("customMessageBg", text))
51
-
52
- box.addChild(
53
- new Text(
54
- `${theme.fg(toneColor, "●")} ${theme.bold(theme.fg("customMessageLabel", report.title))}`,
55
- 0,
56
- 0,
57
- ),
58
- )
59
-
60
- if (report.headline) {
61
- box.addChild(new Text(theme.fg("text", report.headline), 0, 0))
62
- }
63
-
64
- if (report.lines.length > 0) {
65
- box.addChild(
66
- new Text(
67
- report.lines.map((line) => theme.fg(lineTextTone, line)).join("\n"),
68
- 0,
69
- 0,
70
- ),
71
- )
72
- }
73
-
74
- if (report.output?.trim()) {
75
- if (!expanded && report.hideOutputWhenCollapsed) {
76
- box.addChild(
77
- new Text(
78
- formatExpandHint(
79
- theme,
80
- `to expand to see ${report.outputDescription ?? "output"}.`,
81
- ),
82
- 0,
83
- 0,
84
- ),
85
- )
86
- } else {
87
- const { text, truncated } = formatReportOutput(report.output, expanded)
88
-
89
- box.addChild(
90
- new Text(
91
- theme.fg(outputTextTone, report.outputLabel ?? "Output:"),
92
- 0,
93
- 0,
94
- ),
95
- )
96
- box.addChild(new Text(theme.fg(outputTextTone, text), 0, 0))
97
-
98
- if (truncated) {
99
- box.addChild(
100
- new Text(
101
- formatExpandHint(
102
- theme,
103
- `to expand to view the full ${report.outputDescription ?? "output"}.`,
104
- ),
105
- 0,
106
- 0,
107
- ),
108
- )
109
- }
110
- }
111
- }
112
-
113
- return box
39
+ return createReportBox(report, theme, { expanded })
114
40
  }
115
41
  }
116
42
 
@@ -343,39 +269,12 @@ export function setPackageManagerWidget(
343
269
 
344
270
  ctx.ui.setWidget(
345
271
  PACKAGE_MANAGER_WIDGET_KEY,
346
- (_tui, theme) => {
347
- const background =
348
- state.mode === "countdown"
349
- ? (text: string) => theme.bg("toolSuccessBg", text)
350
- : (text: string) => theme.bg("toolPendingBg", text)
351
- const box = new Box(1, 1, background)
352
- const titleColor = state.mode === "countdown" ? "success" : "accent"
353
- const bodyLines = createAutomaticUpdateWidgetLines(state)
354
-
355
- box.addChild(
356
- new Text(
357
- `${theme.fg(titleColor, "●")} ${theme.bold(theme.fg("customMessageLabel", PACKAGE_MANAGER_TITLE))}`,
358
- 0,
359
- 0,
360
- ),
361
- )
362
- box.addChild(
363
- new Text(
364
- bodyLines
365
- .map((line, index) =>
366
- theme.fg(
367
- index === bodyLines.length - 1 ? "dim" : "text",
368
- line,
369
- ),
370
- )
371
- .join("\n"),
372
- 0,
373
- 0,
374
- ),
375
- )
376
-
377
- return box
378
- },
272
+ (_tui, theme) =>
273
+ createStatusBox(theme, createAutomaticUpdateWidgetLines(state), {
274
+ titleColor: state.mode === "countdown" ? "success" : "accent",
275
+ background:
276
+ state.mode === "countdown" ? "toolSuccessBg" : "toolPendingBg",
277
+ }),
379
278
  { placement: "aboveEditor" },
380
279
  )
381
280
  }
@@ -428,6 +327,115 @@ function formatAutomaticUpdateHeadline(
428
327
  return `Pi package(s) update ${suffix}.`
429
328
  }
430
329
 
330
+ function createReportBox(
331
+ report: PackageManagerReport,
332
+ theme: ThemeLike,
333
+ options: { expanded: boolean },
334
+ ): Box {
335
+ const toneColor =
336
+ report.tone === "error"
337
+ ? "error"
338
+ : report.tone === "warning"
339
+ ? "warning"
340
+ : report.tone === "success"
341
+ ? "success"
342
+ : "accent"
343
+ const lineTextTone = report.lineTone === "dim" ? "dim" : "customMessageText"
344
+ const outputTextTone = report.outputTone === "dim" ? "dim" : lineTextTone
345
+ const box = new Box(1, 1, (text: string) => theme.bg("customMessageBg", text))
346
+
347
+ box.addChild(
348
+ new Text(
349
+ `${theme.fg(toneColor, "●")} ${theme.bold(theme.fg("customMessageLabel", report.title))}`,
350
+ 0,
351
+ 0,
352
+ ),
353
+ )
354
+
355
+ if (report.headline) {
356
+ box.addChild(new Text(theme.fg("text", report.headline), 0, 0))
357
+ }
358
+
359
+ if (report.lines.length > 0) {
360
+ box.addChild(
361
+ new Text(
362
+ report.lines.map((line) => theme.fg(lineTextTone, line)).join("\n"),
363
+ 0,
364
+ 0,
365
+ ),
366
+ )
367
+ }
368
+
369
+ if (!report.output?.trim()) {
370
+ return box
371
+ }
372
+
373
+ if (!options.expanded && report.hideOutputWhenCollapsed) {
374
+ box.addChild(
375
+ new Text(
376
+ formatExpandHint(
377
+ theme,
378
+ `to expand to see ${report.outputDescription ?? "output"}.`,
379
+ ),
380
+ 0,
381
+ 0,
382
+ ),
383
+ )
384
+ return box
385
+ }
386
+
387
+ const { text, truncated } = formatReportOutput(report.output, options.expanded)
388
+
389
+ box.addChild(
390
+ new Text(theme.fg(outputTextTone, report.outputLabel ?? "Output:"), 0, 0),
391
+ )
392
+ box.addChild(new Text(theme.fg(outputTextTone, text), 0, 0))
393
+
394
+ if (truncated) {
395
+ box.addChild(
396
+ new Text(
397
+ formatExpandHint(
398
+ theme,
399
+ `to expand to view the full ${report.outputDescription ?? "output"}.`,
400
+ ),
401
+ 0,
402
+ 0,
403
+ ),
404
+ )
405
+ }
406
+
407
+ return box
408
+ }
409
+
410
+ function createStatusBox(
411
+ theme: ThemeLike,
412
+ bodyLines: string[],
413
+ options: { titleColor: string; background: string },
414
+ ): Box {
415
+ const box = new Box(1, 1, (text: string) => theme.bg(options.background, text))
416
+
417
+ box.addChild(
418
+ new Text(
419
+ `${theme.fg(options.titleColor, "●")} ${theme.bold(theme.fg("customMessageLabel", PACKAGE_MANAGER_TITLE))}`,
420
+ 0,
421
+ 0,
422
+ ),
423
+ )
424
+ box.addChild(
425
+ new Text(
426
+ bodyLines
427
+ .map((line, index) =>
428
+ theme.fg(index === bodyLines.length - 1 ? "dim" : "text", line),
429
+ )
430
+ .join("\n"),
431
+ 0,
432
+ 0,
433
+ ),
434
+ )
435
+
436
+ return box
437
+ }
438
+
431
439
  function formatExpandHint(theme: ThemeLike, description: string): string {
432
440
  const expandKey = keyText("app.tools.expand") || "Ctrl+O"
433
441
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gaosh3n/pi-package-manager",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Package Manager for Pi",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@11.23.0",