@gaosh3n/pi-package-manager 0.1.1 → 0.1.2
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 +11 -0
- package/index.ts +10 -3
- package/internal/controller.ts +75 -0
- package/internal/model.ts +4 -0
- package/internal/reports.ts +51 -6
- package/internal/runtime.ts +16 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
- a visible progress UI while Pi checks or updates packages
|
|
9
9
|
- `/package-manager status` to see whether updates are available
|
|
10
10
|
- `/package-manager update` to run the update flow on demand
|
|
11
|
+
- `/package-manager install` to install a Pi package from an entered source
|
|
11
12
|
- a final result card in Pi so you can review the latest outcome
|
|
12
13
|
- automatic Pi reload after a successful startup update
|
|
13
14
|
|
|
@@ -44,3 +45,13 @@ Run:
|
|
|
44
45
|
```
|
|
45
46
|
|
|
46
47
|
Pi will run the update flow for you, show live progress, and record the final result in the transcript.
|
|
48
|
+
|
|
49
|
+
### Install a package
|
|
50
|
+
|
|
51
|
+
Run:
|
|
52
|
+
|
|
53
|
+
```text
|
|
54
|
+
/package-manager install
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Pi will prompt you for a package source such as `npm:@foo/bar` or `git:github.com/user/repo`, run the native `pi install ...` flow, and show a final result card. After a successful install, run `/reload` to activate the installed package resources.
|
package/index.ts
CHANGED
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
import {
|
|
26
26
|
createAutomaticUpdateWidgetLines,
|
|
27
27
|
createAutoUpdateResultReport,
|
|
28
|
+
createInstallResultReport,
|
|
28
29
|
createReportEntryRenderer,
|
|
29
30
|
createStatusReport,
|
|
30
31
|
formatStatusLines,
|
|
@@ -50,9 +51,9 @@ export default function initPackageManager(
|
|
|
50
51
|
|
|
51
52
|
pi.registerCommand("package-manager", {
|
|
52
53
|
description:
|
|
53
|
-
"Manage Pi
|
|
54
|
+
"Manage Pi packages (usage: /package-manager [status|update|install])",
|
|
54
55
|
getArgumentCompletions: (prefix: string): AutocompleteItem[] | null => {
|
|
55
|
-
const items = ["status", "update"].map((value) => ({
|
|
56
|
+
const items = ["status", "update", "install"].map((value) => ({
|
|
56
57
|
value,
|
|
57
58
|
label: value,
|
|
58
59
|
}))
|
|
@@ -78,7 +79,12 @@ export default function initPackageManager(
|
|
|
78
79
|
return
|
|
79
80
|
}
|
|
80
81
|
|
|
81
|
-
|
|
82
|
+
if (subcommand === "install") {
|
|
83
|
+
await controller.handleInstall(ctx)
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
ctx.ui.notify("Usage: /package-manager [status|update|install]", "warning")
|
|
82
88
|
},
|
|
83
89
|
})
|
|
84
90
|
}
|
|
@@ -90,6 +96,7 @@ export {
|
|
|
90
96
|
createAutomaticUpdateWidgetLines,
|
|
91
97
|
createAutoUpdateRecord,
|
|
92
98
|
createAutoUpdateResultReport,
|
|
99
|
+
createInstallResultReport,
|
|
93
100
|
createStatusReport,
|
|
94
101
|
formatStatusLines,
|
|
95
102
|
formatUtcTimestamp,
|
package/internal/controller.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
import {
|
|
15
15
|
clearPackageManagerWidget,
|
|
16
16
|
createAutoUpdateResultReport,
|
|
17
|
+
createInstallResultReport,
|
|
17
18
|
createStatusErrorReport,
|
|
18
19
|
createStatusReport,
|
|
19
20
|
getExecDisplayOutput,
|
|
@@ -30,6 +31,7 @@ export function createPackageManagerController(
|
|
|
30
31
|
onSessionStart,
|
|
31
32
|
handleStatus,
|
|
32
33
|
handleUpdate,
|
|
34
|
+
handleInstall,
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
async function onSessionStart(
|
|
@@ -165,6 +167,79 @@ export function createPackageManagerController(
|
|
|
165
167
|
}
|
|
166
168
|
}
|
|
167
169
|
|
|
170
|
+
async function handleInstall(ctx: ExtensionCommandContext): Promise<void> {
|
|
171
|
+
if (!ctx.hasUI) {
|
|
172
|
+
ctx.ui.notify(
|
|
173
|
+
"/package-manager install requires dialog-capable UI.",
|
|
174
|
+
"warning",
|
|
175
|
+
)
|
|
176
|
+
return
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const source = (
|
|
180
|
+
await ctx.ui.input(
|
|
181
|
+
"Install Pi Package",
|
|
182
|
+
"npm:@scope/pkg or git:github.com/user/repo",
|
|
183
|
+
)
|
|
184
|
+
)?.trim()
|
|
185
|
+
|
|
186
|
+
if (source === undefined) {
|
|
187
|
+
return
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (!source) {
|
|
191
|
+
ctx.ui.notify("Package source is required.", "warning")
|
|
192
|
+
return
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const startedAtUtc = deps.nowIso()
|
|
196
|
+
setPackageManagerWidget(ctx, { mode: "package-installing", source })
|
|
197
|
+
|
|
198
|
+
try {
|
|
199
|
+
const result = await deps.runNativeInstall(pi, ctx, source)
|
|
200
|
+
const output = getExecDisplayOutput(result)
|
|
201
|
+
|
|
202
|
+
if (result.code === 0) {
|
|
203
|
+
pi.appendEntry(
|
|
204
|
+
REPORT_ENTRY_TYPE,
|
|
205
|
+
createInstallResultReport({
|
|
206
|
+
startedAtUtc,
|
|
207
|
+
endedAtUtc: deps.nowIso(),
|
|
208
|
+
source,
|
|
209
|
+
outcome: "succeeded",
|
|
210
|
+
output,
|
|
211
|
+
}),
|
|
212
|
+
)
|
|
213
|
+
return
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
pi.appendEntry(
|
|
217
|
+
REPORT_ENTRY_TYPE,
|
|
218
|
+
createInstallResultReport({
|
|
219
|
+
startedAtUtc,
|
|
220
|
+
endedAtUtc: deps.nowIso(),
|
|
221
|
+
source,
|
|
222
|
+
outcome: "failed",
|
|
223
|
+
output,
|
|
224
|
+
reason: getExecFailureDetail(result),
|
|
225
|
+
}),
|
|
226
|
+
)
|
|
227
|
+
} catch (error) {
|
|
228
|
+
pi.appendEntry(
|
|
229
|
+
REPORT_ENTRY_TYPE,
|
|
230
|
+
createInstallResultReport({
|
|
231
|
+
startedAtUtc,
|
|
232
|
+
endedAtUtc: deps.nowIso(),
|
|
233
|
+
source,
|
|
234
|
+
outcome: "failed",
|
|
235
|
+
reason: getErrorMessage(error),
|
|
236
|
+
}),
|
|
237
|
+
)
|
|
238
|
+
} finally {
|
|
239
|
+
clearPackageManagerWidget(ctx)
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
168
243
|
function appendSkippedResult(startedAtUtc: string, reason: string): void {
|
|
169
244
|
const record = createAutoUpdateRecord({
|
|
170
245
|
startedAtUtc,
|
package/internal/model.ts
CHANGED
|
@@ -5,8 +5,10 @@ export const PACKAGE_MANAGER_WIDGET_KEY = "pi-package-manager"
|
|
|
5
5
|
export const RELOAD_COUNTDOWN_SECONDS = 5
|
|
6
6
|
export const OUTPUT_PREVIEW_LINE_COUNT = 8
|
|
7
7
|
export const UPDATE_COMMAND = ["update", "--extensions"] as const
|
|
8
|
+
export const INSTALL_COMMAND = ["install"] as const
|
|
8
9
|
|
|
9
10
|
export type AutoUpdateOutcome = "succeeded" | "failed" | "skipped"
|
|
11
|
+
export type InstallOutcome = "succeeded" | "failed"
|
|
10
12
|
export type ReportTone = "info" | "success" | "warning" | "error"
|
|
11
13
|
|
|
12
14
|
export type WidgetState =
|
|
@@ -14,6 +16,7 @@ export type WidgetState =
|
|
|
14
16
|
| { mode: "checking" }
|
|
15
17
|
| { mode: "installing"; packages: number }
|
|
16
18
|
| { mode: "countdown"; secondsRemaining: number }
|
|
19
|
+
| { mode: "package-installing"; source: string }
|
|
17
20
|
|
|
18
21
|
export interface AutoUpdateRecord {
|
|
19
22
|
startedAtUtc: string
|
|
@@ -31,6 +34,7 @@ export interface PackageManagerReport {
|
|
|
31
34
|
lineTone?: "default" | "dim"
|
|
32
35
|
output?: string
|
|
33
36
|
outputLabel?: string
|
|
37
|
+
outputDescription?: string
|
|
34
38
|
outputTone?: "default" | "dim"
|
|
35
39
|
hideOutputWhenCollapsed?: boolean
|
|
36
40
|
}
|
package/internal/reports.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
PACKAGE_MANAGER_WIDGET_KEY,
|
|
12
12
|
type AutoUpdateOutcome,
|
|
13
13
|
type AutoUpdateRecord,
|
|
14
|
+
type InstallOutcome,
|
|
14
15
|
type PackageManagerReport,
|
|
15
16
|
type PackageStatusSnapshot,
|
|
16
17
|
type WidgetState,
|
|
@@ -73,7 +74,10 @@ export function createReportEntryRenderer() {
|
|
|
73
74
|
if (!expanded && report.hideOutputWhenCollapsed) {
|
|
74
75
|
box.addChild(
|
|
75
76
|
new Text(
|
|
76
|
-
formatExpandHint(
|
|
77
|
+
formatExpandHint(
|
|
78
|
+
theme,
|
|
79
|
+
`to expand to see ${report.outputDescription ?? "output"}.`,
|
|
80
|
+
),
|
|
77
81
|
0,
|
|
78
82
|
0,
|
|
79
83
|
),
|
|
@@ -83,10 +87,7 @@ export function createReportEntryRenderer() {
|
|
|
83
87
|
|
|
84
88
|
box.addChild(
|
|
85
89
|
new Text(
|
|
86
|
-
theme.fg(
|
|
87
|
-
outputTextTone,
|
|
88
|
-
report.outputLabel ?? "Update output:",
|
|
89
|
-
),
|
|
90
|
+
theme.fg(outputTextTone, report.outputLabel ?? "Output:"),
|
|
90
91
|
0,
|
|
91
92
|
0,
|
|
92
93
|
),
|
|
@@ -98,7 +99,7 @@ export function createReportEntryRenderer() {
|
|
|
98
99
|
new Text(
|
|
99
100
|
formatExpandHint(
|
|
100
101
|
theme,
|
|
101
|
-
|
|
102
|
+
`to expand to view the full ${report.outputDescription ?? "output"}.`,
|
|
102
103
|
),
|
|
103
104
|
0,
|
|
104
105
|
0,
|
|
@@ -203,6 +204,43 @@ export function createAutoUpdateResultReport(input: {
|
|
|
203
204
|
lines,
|
|
204
205
|
lineTone: "dim",
|
|
205
206
|
output: input.output ?? input.record.reason,
|
|
207
|
+
outputLabel: "Update output:",
|
|
208
|
+
outputDescription: "update output",
|
|
209
|
+
outputTone: "dim",
|
|
210
|
+
hideOutputWhenCollapsed: true,
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function createInstallResultReport(input: {
|
|
215
|
+
startedAtUtc: string
|
|
216
|
+
endedAtUtc: string
|
|
217
|
+
source: string
|
|
218
|
+
outcome: InstallOutcome
|
|
219
|
+
output?: string
|
|
220
|
+
reason?: string
|
|
221
|
+
}): PackageManagerReport {
|
|
222
|
+
return {
|
|
223
|
+
title: PACKAGE_MANAGER_TITLE,
|
|
224
|
+
headline:
|
|
225
|
+
input.outcome === "succeeded"
|
|
226
|
+
? "Pi package install completed."
|
|
227
|
+
: "Pi package install failed.",
|
|
228
|
+
tone: input.outcome === "succeeded" ? "success" : "error",
|
|
229
|
+
lines: [
|
|
230
|
+
`Start: ${formatUtcTimestamp(input.startedAtUtc)}`,
|
|
231
|
+
`End: ${formatUtcTimestamp(input.endedAtUtc)}`,
|
|
232
|
+
`Result: ${input.outcome}`,
|
|
233
|
+
`Package source: ${input.source}`,
|
|
234
|
+
...(input.outcome === "succeeded"
|
|
235
|
+
? ["Run /reload to activate installed package resources."]
|
|
236
|
+
: []),
|
|
237
|
+
],
|
|
238
|
+
lineTone: "dim",
|
|
239
|
+
output: input.output ?? input.reason,
|
|
240
|
+
outputLabel:
|
|
241
|
+
input.outcome === "succeeded" ? "Install output:" : "Error detail:",
|
|
242
|
+
outputDescription:
|
|
243
|
+
input.outcome === "succeeded" ? "install output" : "error detail",
|
|
206
244
|
outputTone: "dim",
|
|
207
245
|
hideOutputWhenCollapsed: true,
|
|
208
246
|
}
|
|
@@ -227,6 +265,13 @@ export function createAutomaticUpdateWidgetLines(state: WidgetState): string[] {
|
|
|
227
265
|
]
|
|
228
266
|
}
|
|
229
267
|
|
|
268
|
+
if (state.mode === "package-installing") {
|
|
269
|
+
return [
|
|
270
|
+
"Pi package install in progress.",
|
|
271
|
+
`Installing package from ${state.source}...`,
|
|
272
|
+
]
|
|
273
|
+
}
|
|
274
|
+
|
|
230
275
|
return [
|
|
231
276
|
formatAutomaticUpdateHeadline("succeeded"),
|
|
232
277
|
`Reloading in ${state.secondsRemaining} second${state.secondsRemaining === 1 ? "" : "s"} to activate updated package resources.`,
|
package/internal/runtime.ts
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
type ExtensionContext,
|
|
9
9
|
} from "@earendil-works/pi-coding-agent"
|
|
10
10
|
|
|
11
|
-
import { UPDATE_COMMAND } from "./model.ts"
|
|
11
|
+
import { INSTALL_COMMAND, UPDATE_COMMAND } from "./model.ts"
|
|
12
12
|
|
|
13
13
|
export interface PackageManagerDeps {
|
|
14
14
|
nowIso(): string
|
|
@@ -19,6 +19,11 @@ export interface PackageManagerDeps {
|
|
|
19
19
|
pi: Pick<ExtensionAPI, "exec">,
|
|
20
20
|
ctx: ExtensionCommandContext,
|
|
21
21
|
): Promise<ExecResult>
|
|
22
|
+
runNativeInstall(
|
|
23
|
+
pi: Pick<ExtensionAPI, "exec">,
|
|
24
|
+
ctx: ExtensionCommandContext,
|
|
25
|
+
source: string,
|
|
26
|
+
): Promise<ExecResult>
|
|
22
27
|
}
|
|
23
28
|
|
|
24
29
|
export const defaultPackageManagerDeps: PackageManagerDeps = {
|
|
@@ -51,4 +56,14 @@ export const defaultPackageManagerDeps: PackageManagerDeps = {
|
|
|
51
56
|
signal: ctx.signal,
|
|
52
57
|
})
|
|
53
58
|
},
|
|
59
|
+
runNativeInstall(
|
|
60
|
+
pi: Pick<ExtensionAPI, "exec">,
|
|
61
|
+
ctx: ExtensionCommandContext,
|
|
62
|
+
source: string,
|
|
63
|
+
) {
|
|
64
|
+
return pi.exec("pi", [...INSTALL_COMMAND, source], {
|
|
65
|
+
cwd: ctx.cwd,
|
|
66
|
+
signal: ctx.signal,
|
|
67
|
+
})
|
|
68
|
+
},
|
|
54
69
|
}
|