@justram/pie 0.2.4 → 0.2.6
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 +32 -0
- package/README.md +14 -0
- package/dist/auth.d.ts +14 -0
- package/dist/auth.js +178 -0
- package/dist/cli/oauth.d.ts +1 -14
- package/dist/cli/oauth.js +1 -178
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/main.js +10 -5
- package/dist/update.d.ts +10 -0
- package/dist/update.js +42 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -12,6 +12,38 @@
|
|
|
12
12
|
|
|
13
13
|
### Removed
|
|
14
14
|
|
|
15
|
+
## 0.2.6
|
|
16
|
+
|
|
17
|
+
### Breaking Changes
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- Added SDK helper utilities for checking updates and formatting update notifications.
|
|
22
|
+
- Added CLI update notification that checks npm registry for newer versions.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
### Removed
|
|
29
|
+
|
|
30
|
+
## 0.2.5
|
|
31
|
+
|
|
32
|
+
### Breaking Changes
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
- Exported OAuth login helpers from the SDK for Node usage.
|
|
37
|
+
- Added the `examples/sdk-login` example to demonstrate SDK-based OAuth login.
|
|
38
|
+
|
|
39
|
+
### Changed
|
|
40
|
+
|
|
41
|
+
- Documented SDK OAuth login usage in the README.
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
|
|
45
|
+
### Removed
|
|
46
|
+
|
|
15
47
|
## 0.2.3
|
|
16
48
|
|
|
17
49
|
### Breaking Changes
|
package/README.md
CHANGED
|
@@ -53,6 +53,20 @@ Run `--login` to authenticate with an OAuth-capable provider. The CLI prints a U
|
|
|
53
53
|
pie --login openai-codex
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
+
#### SDK login (Node-only)
|
|
57
|
+
|
|
58
|
+
SDK users can trigger the same OAuth flow programmatically (uses Node prompts and writes to
|
|
59
|
+
`~/.pi/agent/auth.json`):
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { loginWithOAuthProvider } from "@justram/pie";
|
|
63
|
+
|
|
64
|
+
await loginWithOAuthProvider("openai-codex", process.stderr);
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
If you only need an API key and want auto-refresh, call `resolveApiKeyForProvider(provider, process.stderr)`.
|
|
68
|
+
These helpers are Node-only and are not intended for browser environments.
|
|
69
|
+
|
|
56
70
|
Supported OAuth providers:
|
|
57
71
|
- `anthropic`
|
|
58
72
|
- `github-copilot`
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Writable } from "node:stream";
|
|
2
|
+
import { type OAuthCredentials } from "@mariozechner/pi-ai";
|
|
3
|
+
export type ApiKeyCredential = {
|
|
4
|
+
type: "api_key";
|
|
5
|
+
key: string;
|
|
6
|
+
};
|
|
7
|
+
export type OAuthCredential = {
|
|
8
|
+
type: "oauth";
|
|
9
|
+
} & OAuthCredentials;
|
|
10
|
+
export type AuthCredential = ApiKeyCredential | OAuthCredential;
|
|
11
|
+
export type AuthMap = Record<string, AuthCredential | undefined>;
|
|
12
|
+
export declare function resolveApiKeyForProvider(provider: string, stderr: Writable): Promise<string | undefined>;
|
|
13
|
+
export declare function getSupportedOAuthProviders(): string[];
|
|
14
|
+
export declare function loginWithOAuthProvider(provider: string, stderr: Writable): Promise<string>;
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { createInterface } from "node:readline/promises";
|
|
5
|
+
import { getEnvApiKey, getOAuthApiKey, getOAuthProviders, loginAnthropic, loginAntigravity, loginGeminiCli, loginGitHubCopilot, loginOpenAICodex, } from "@mariozechner/pi-ai";
|
|
6
|
+
const AUTH_PATH = join(homedir(), ".pi", "agent", "auth.json");
|
|
7
|
+
const OAUTH_PROVIDERS = new Set(getOAuthProviders().map((provider) => provider.id));
|
|
8
|
+
export async function resolveApiKeyForProvider(provider, stderr) {
|
|
9
|
+
const auth = loadAuth();
|
|
10
|
+
const authKey = await getKeyFromAuth(provider, auth);
|
|
11
|
+
if (authKey) {
|
|
12
|
+
return authKey;
|
|
13
|
+
}
|
|
14
|
+
const envKey = getEnvApiKey(provider);
|
|
15
|
+
if (envKey) {
|
|
16
|
+
return envKey;
|
|
17
|
+
}
|
|
18
|
+
if (!isOAuthProvider(provider)) {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
return await loginAndResolve(provider, auth, stderr);
|
|
22
|
+
}
|
|
23
|
+
export function getSupportedOAuthProviders() {
|
|
24
|
+
return getOAuthProviders()
|
|
25
|
+
.map((provider) => provider.id)
|
|
26
|
+
.sort();
|
|
27
|
+
}
|
|
28
|
+
export async function loginWithOAuthProvider(provider, stderr) {
|
|
29
|
+
if (!isOAuthProvider(provider)) {
|
|
30
|
+
const supported = getSupportedOAuthProviders().join(", ");
|
|
31
|
+
throw new Error(`Unsupported OAuth provider: ${provider}. Supported: ${supported}`);
|
|
32
|
+
}
|
|
33
|
+
const auth = loadAuth();
|
|
34
|
+
return await loginAndResolve(provider, auth, stderr);
|
|
35
|
+
}
|
|
36
|
+
function loadAuth() {
|
|
37
|
+
try {
|
|
38
|
+
const raw = readFileSync(AUTH_PATH, "utf-8");
|
|
39
|
+
const parsed = JSON.parse(raw);
|
|
40
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return {};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function saveAuth(auth) {
|
|
47
|
+
const dir = dirname(AUTH_PATH);
|
|
48
|
+
if (!existsSync(dir)) {
|
|
49
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
50
|
+
}
|
|
51
|
+
writeFileSync(AUTH_PATH, JSON.stringify(auth, null, 2), "utf-8");
|
|
52
|
+
chmodSync(AUTH_PATH, 0o600);
|
|
53
|
+
}
|
|
54
|
+
async function getKeyFromAuth(provider, auth) {
|
|
55
|
+
const entry = auth[provider];
|
|
56
|
+
if (!entry) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
if (entry.type === "api_key") {
|
|
60
|
+
return entry.key;
|
|
61
|
+
}
|
|
62
|
+
if (!isOAuthProvider(provider)) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
const result = await getOAuthApiKey(provider, oauthCredentials(auth));
|
|
66
|
+
if (!result) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
auth[provider] = { type: "oauth", ...result.newCredentials };
|
|
70
|
+
saveAuth(auth);
|
|
71
|
+
return result.apiKey;
|
|
72
|
+
}
|
|
73
|
+
async function loginAndResolve(provider, auth, stderr) {
|
|
74
|
+
const result = await getOAuthApiKey(provider, oauthCredentials(auth));
|
|
75
|
+
if (result) {
|
|
76
|
+
auth[provider] = { type: "oauth", ...result.newCredentials };
|
|
77
|
+
saveAuth(auth);
|
|
78
|
+
return result.apiKey;
|
|
79
|
+
}
|
|
80
|
+
writeLine(stderr, `[oauth:${provider}] No credentials found in ${AUTH_PATH}. Starting login...`);
|
|
81
|
+
const credentials = await login(provider, stderr);
|
|
82
|
+
auth[provider] = { type: "oauth", ...credentials };
|
|
83
|
+
saveAuth(auth);
|
|
84
|
+
const refreshed = await getOAuthApiKey(provider, oauthCredentials(auth));
|
|
85
|
+
if (!refreshed) {
|
|
86
|
+
throw new Error(`OAuth login completed, but no credentials were returned for ${provider}`);
|
|
87
|
+
}
|
|
88
|
+
auth[provider] = { type: "oauth", ...refreshed.newCredentials };
|
|
89
|
+
saveAuth(auth);
|
|
90
|
+
return refreshed.apiKey;
|
|
91
|
+
}
|
|
92
|
+
function oauthCredentials(auth) {
|
|
93
|
+
const result = {};
|
|
94
|
+
for (const [key, value] of Object.entries(auth)) {
|
|
95
|
+
if (value?.type === "oauth") {
|
|
96
|
+
result[key] = value;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
function isOAuthProvider(provider) {
|
|
102
|
+
return OAUTH_PROVIDERS.has(provider);
|
|
103
|
+
}
|
|
104
|
+
async function login(provider, stderr) {
|
|
105
|
+
switch (provider) {
|
|
106
|
+
case "anthropic":
|
|
107
|
+
return await loginAnthropic((url) => {
|
|
108
|
+
writeLine(stderr, `[oauth:${provider}] Open this URL to authenticate:`);
|
|
109
|
+
writeLine(stderr, url);
|
|
110
|
+
}, async () => {
|
|
111
|
+
return await promptOnce("Paste the authorization code:", stderr);
|
|
112
|
+
});
|
|
113
|
+
case "github-copilot":
|
|
114
|
+
return await loginGitHubCopilot({
|
|
115
|
+
onAuth: (url, instructions) => {
|
|
116
|
+
writeLine(stderr, `[oauth:${provider}] Open this URL to authenticate:`);
|
|
117
|
+
writeLine(stderr, url);
|
|
118
|
+
if (instructions)
|
|
119
|
+
writeLine(stderr, instructions);
|
|
120
|
+
},
|
|
121
|
+
onPrompt: async ({ message }) => {
|
|
122
|
+
return await promptOnce(message, stderr);
|
|
123
|
+
},
|
|
124
|
+
onProgress: (message) => {
|
|
125
|
+
writeLine(stderr, `[oauth:${provider}] ${message}`);
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
case "google-gemini-cli":
|
|
129
|
+
return await loginGeminiCli(({ url, instructions }) => {
|
|
130
|
+
writeLine(stderr, `[oauth:${provider}] Open this URL to authenticate:`);
|
|
131
|
+
writeLine(stderr, url);
|
|
132
|
+
if (instructions)
|
|
133
|
+
writeLine(stderr, instructions);
|
|
134
|
+
}, (message) => {
|
|
135
|
+
writeLine(stderr, `[oauth:${provider}] ${message}`);
|
|
136
|
+
}, async () => {
|
|
137
|
+
return await promptOnce("Paste the full redirect URL (or press Enter to wait for browser callback):", stderr);
|
|
138
|
+
});
|
|
139
|
+
case "google-antigravity":
|
|
140
|
+
return await loginAntigravity(({ url, instructions }) => {
|
|
141
|
+
writeLine(stderr, `[oauth:${provider}] Open this URL to authenticate:`);
|
|
142
|
+
writeLine(stderr, url);
|
|
143
|
+
if (instructions)
|
|
144
|
+
writeLine(stderr, instructions);
|
|
145
|
+
}, (message) => {
|
|
146
|
+
writeLine(stderr, `[oauth:${provider}] ${message}`);
|
|
147
|
+
}, async () => {
|
|
148
|
+
return await promptOnce("Paste the full redirect URL (or press Enter to wait for browser callback):", stderr);
|
|
149
|
+
});
|
|
150
|
+
case "openai-codex":
|
|
151
|
+
return await loginOpenAICodex({
|
|
152
|
+
onAuth: ({ url, instructions }) => {
|
|
153
|
+
writeLine(stderr, `[oauth:${provider}] Open this URL to authenticate:`);
|
|
154
|
+
writeLine(stderr, url);
|
|
155
|
+
if (instructions)
|
|
156
|
+
writeLine(stderr, instructions);
|
|
157
|
+
},
|
|
158
|
+
onPrompt: async ({ message }) => {
|
|
159
|
+
return await promptOnce(message, stderr);
|
|
160
|
+
},
|
|
161
|
+
onProgress: (message) => {
|
|
162
|
+
writeLine(stderr, `[oauth:${provider}] ${message}`);
|
|
163
|
+
},
|
|
164
|
+
onManualCodeInput: async () => {
|
|
165
|
+
return await promptOnce("Paste the authorization code:", stderr);
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
default:
|
|
169
|
+
throw new Error(`OAuth login flow not implemented for provider: ${provider}.`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
async function promptOnce(prompt, stderr) {
|
|
173
|
+
const rl = createInterface({ input: process.stdin, output: stderr });
|
|
174
|
+
return rl.question(`${prompt} `).finally(() => rl.close());
|
|
175
|
+
}
|
|
176
|
+
function writeLine(stream, message) {
|
|
177
|
+
stream.write(`${message}\n`);
|
|
178
|
+
}
|
package/dist/cli/oauth.d.ts
CHANGED
|
@@ -1,14 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { type OAuthCredentials } from "@mariozechner/pi-ai";
|
|
3
|
-
export type ApiKeyCredential = {
|
|
4
|
-
type: "api_key";
|
|
5
|
-
key: string;
|
|
6
|
-
};
|
|
7
|
-
export type OAuthCredential = {
|
|
8
|
-
type: "oauth";
|
|
9
|
-
} & OAuthCredentials;
|
|
10
|
-
export type AuthCredential = ApiKeyCredential | OAuthCredential;
|
|
11
|
-
export type AuthMap = Record<string, AuthCredential | undefined>;
|
|
12
|
-
export declare function resolveApiKeyForProvider(provider: string, stderr: Writable): Promise<string | undefined>;
|
|
13
|
-
export declare function getSupportedOAuthProviders(): string[];
|
|
14
|
-
export declare function loginWithOAuthProvider(provider: string, stderr: Writable): Promise<string>;
|
|
1
|
+
export { type ApiKeyCredential, type AuthCredential, type AuthMap, getSupportedOAuthProviders, loginWithOAuthProvider, type OAuthCredential, resolveApiKeyForProvider, } from "../auth.js";
|
package/dist/cli/oauth.js
CHANGED
|
@@ -1,178 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { homedir } from "node:os";
|
|
3
|
-
import { dirname, join } from "node:path";
|
|
4
|
-
import { createInterface } from "node:readline/promises";
|
|
5
|
-
import { getEnvApiKey, getOAuthApiKey, getOAuthProviders, loginAnthropic, loginAntigravity, loginGeminiCli, loginGitHubCopilot, loginOpenAICodex, } from "@mariozechner/pi-ai";
|
|
6
|
-
const AUTH_PATH = join(homedir(), ".pi", "agent", "auth.json");
|
|
7
|
-
const OAUTH_PROVIDERS = new Set(getOAuthProviders().map((provider) => provider.id));
|
|
8
|
-
export async function resolveApiKeyForProvider(provider, stderr) {
|
|
9
|
-
const auth = loadAuth();
|
|
10
|
-
const authKey = await getKeyFromAuth(provider, auth);
|
|
11
|
-
if (authKey) {
|
|
12
|
-
return authKey;
|
|
13
|
-
}
|
|
14
|
-
const envKey = getEnvApiKey(provider);
|
|
15
|
-
if (envKey) {
|
|
16
|
-
return envKey;
|
|
17
|
-
}
|
|
18
|
-
if (!isOAuthProvider(provider)) {
|
|
19
|
-
return undefined;
|
|
20
|
-
}
|
|
21
|
-
return await loginAndResolve(provider, auth, stderr);
|
|
22
|
-
}
|
|
23
|
-
export function getSupportedOAuthProviders() {
|
|
24
|
-
return getOAuthProviders()
|
|
25
|
-
.map((provider) => provider.id)
|
|
26
|
-
.sort();
|
|
27
|
-
}
|
|
28
|
-
export async function loginWithOAuthProvider(provider, stderr) {
|
|
29
|
-
if (!isOAuthProvider(provider)) {
|
|
30
|
-
const supported = getSupportedOAuthProviders().join(", ");
|
|
31
|
-
throw new Error(`Unsupported OAuth provider: ${provider}. Supported: ${supported}`);
|
|
32
|
-
}
|
|
33
|
-
const auth = loadAuth();
|
|
34
|
-
return await loginAndResolve(provider, auth, stderr);
|
|
35
|
-
}
|
|
36
|
-
function loadAuth() {
|
|
37
|
-
try {
|
|
38
|
-
const raw = readFileSync(AUTH_PATH, "utf-8");
|
|
39
|
-
const parsed = JSON.parse(raw);
|
|
40
|
-
return parsed && typeof parsed === "object" ? parsed : {};
|
|
41
|
-
}
|
|
42
|
-
catch {
|
|
43
|
-
return {};
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
function saveAuth(auth) {
|
|
47
|
-
const dir = dirname(AUTH_PATH);
|
|
48
|
-
if (!existsSync(dir)) {
|
|
49
|
-
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
50
|
-
}
|
|
51
|
-
writeFileSync(AUTH_PATH, JSON.stringify(auth, null, 2), "utf-8");
|
|
52
|
-
chmodSync(AUTH_PATH, 0o600);
|
|
53
|
-
}
|
|
54
|
-
async function getKeyFromAuth(provider, auth) {
|
|
55
|
-
const entry = auth[provider];
|
|
56
|
-
if (!entry) {
|
|
57
|
-
return undefined;
|
|
58
|
-
}
|
|
59
|
-
if (entry.type === "api_key") {
|
|
60
|
-
return entry.key;
|
|
61
|
-
}
|
|
62
|
-
if (!isOAuthProvider(provider)) {
|
|
63
|
-
return undefined;
|
|
64
|
-
}
|
|
65
|
-
const result = await getOAuthApiKey(provider, oauthCredentials(auth));
|
|
66
|
-
if (!result) {
|
|
67
|
-
return undefined;
|
|
68
|
-
}
|
|
69
|
-
auth[provider] = { type: "oauth", ...result.newCredentials };
|
|
70
|
-
saveAuth(auth);
|
|
71
|
-
return result.apiKey;
|
|
72
|
-
}
|
|
73
|
-
async function loginAndResolve(provider, auth, stderr) {
|
|
74
|
-
const result = await getOAuthApiKey(provider, oauthCredentials(auth));
|
|
75
|
-
if (result) {
|
|
76
|
-
auth[provider] = { type: "oauth", ...result.newCredentials };
|
|
77
|
-
saveAuth(auth);
|
|
78
|
-
return result.apiKey;
|
|
79
|
-
}
|
|
80
|
-
writeLine(stderr, `[oauth:${provider}] No credentials found in ${AUTH_PATH}. Starting login...`);
|
|
81
|
-
const credentials = await login(provider, stderr);
|
|
82
|
-
auth[provider] = { type: "oauth", ...credentials };
|
|
83
|
-
saveAuth(auth);
|
|
84
|
-
const refreshed = await getOAuthApiKey(provider, oauthCredentials(auth));
|
|
85
|
-
if (!refreshed) {
|
|
86
|
-
throw new Error(`OAuth login completed, but no credentials were returned for ${provider}`);
|
|
87
|
-
}
|
|
88
|
-
auth[provider] = { type: "oauth", ...refreshed.newCredentials };
|
|
89
|
-
saveAuth(auth);
|
|
90
|
-
return refreshed.apiKey;
|
|
91
|
-
}
|
|
92
|
-
function oauthCredentials(auth) {
|
|
93
|
-
const result = {};
|
|
94
|
-
for (const [key, value] of Object.entries(auth)) {
|
|
95
|
-
if (value?.type === "oauth") {
|
|
96
|
-
result[key] = value;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
return result;
|
|
100
|
-
}
|
|
101
|
-
function isOAuthProvider(provider) {
|
|
102
|
-
return OAUTH_PROVIDERS.has(provider);
|
|
103
|
-
}
|
|
104
|
-
async function login(provider, stderr) {
|
|
105
|
-
switch (provider) {
|
|
106
|
-
case "anthropic":
|
|
107
|
-
return await loginAnthropic((url) => {
|
|
108
|
-
writeLine(stderr, `[oauth:${provider}] Open this URL to authenticate:`);
|
|
109
|
-
writeLine(stderr, url);
|
|
110
|
-
}, async () => {
|
|
111
|
-
return await promptOnce("Paste the authorization code:", stderr);
|
|
112
|
-
});
|
|
113
|
-
case "github-copilot":
|
|
114
|
-
return await loginGitHubCopilot({
|
|
115
|
-
onAuth: (url, instructions) => {
|
|
116
|
-
writeLine(stderr, `[oauth:${provider}] Open this URL to authenticate:`);
|
|
117
|
-
writeLine(stderr, url);
|
|
118
|
-
if (instructions)
|
|
119
|
-
writeLine(stderr, instructions);
|
|
120
|
-
},
|
|
121
|
-
onPrompt: async ({ message }) => {
|
|
122
|
-
return await promptOnce(message, stderr);
|
|
123
|
-
},
|
|
124
|
-
onProgress: (message) => {
|
|
125
|
-
writeLine(stderr, `[oauth:${provider}] ${message}`);
|
|
126
|
-
},
|
|
127
|
-
});
|
|
128
|
-
case "google-gemini-cli":
|
|
129
|
-
return await loginGeminiCli(({ url, instructions }) => {
|
|
130
|
-
writeLine(stderr, `[oauth:${provider}] Open this URL to authenticate:`);
|
|
131
|
-
writeLine(stderr, url);
|
|
132
|
-
if (instructions)
|
|
133
|
-
writeLine(stderr, instructions);
|
|
134
|
-
}, (message) => {
|
|
135
|
-
writeLine(stderr, `[oauth:${provider}] ${message}`);
|
|
136
|
-
}, async () => {
|
|
137
|
-
return await promptOnce("Paste the full redirect URL (or press Enter to wait for browser callback):", stderr);
|
|
138
|
-
});
|
|
139
|
-
case "google-antigravity":
|
|
140
|
-
return await loginAntigravity(({ url, instructions }) => {
|
|
141
|
-
writeLine(stderr, `[oauth:${provider}] Open this URL to authenticate:`);
|
|
142
|
-
writeLine(stderr, url);
|
|
143
|
-
if (instructions)
|
|
144
|
-
writeLine(stderr, instructions);
|
|
145
|
-
}, (message) => {
|
|
146
|
-
writeLine(stderr, `[oauth:${provider}] ${message}`);
|
|
147
|
-
}, async () => {
|
|
148
|
-
return await promptOnce("Paste the full redirect URL (or press Enter to wait for browser callback):", stderr);
|
|
149
|
-
});
|
|
150
|
-
case "openai-codex":
|
|
151
|
-
return await loginOpenAICodex({
|
|
152
|
-
onAuth: ({ url, instructions }) => {
|
|
153
|
-
writeLine(stderr, `[oauth:${provider}] Open this URL to authenticate:`);
|
|
154
|
-
writeLine(stderr, url);
|
|
155
|
-
if (instructions)
|
|
156
|
-
writeLine(stderr, instructions);
|
|
157
|
-
},
|
|
158
|
-
onPrompt: async ({ message }) => {
|
|
159
|
-
return await promptOnce(message, stderr);
|
|
160
|
-
},
|
|
161
|
-
onProgress: (message) => {
|
|
162
|
-
writeLine(stderr, `[oauth:${provider}] ${message}`);
|
|
163
|
-
},
|
|
164
|
-
onManualCodeInput: async () => {
|
|
165
|
-
return await promptOnce("Paste the authorization code:", stderr);
|
|
166
|
-
},
|
|
167
|
-
});
|
|
168
|
-
default:
|
|
169
|
-
throw new Error(`OAuth login flow not implemented for provider: ${provider}.`);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
async function promptOnce(prompt, stderr) {
|
|
173
|
-
const rl = createInterface({ input: process.stdin, output: stderr });
|
|
174
|
-
return rl.question(`${prompt} `).finally(() => rl.close());
|
|
175
|
-
}
|
|
176
|
-
function writeLine(stream, message) {
|
|
177
|
-
stream.write(`${message}\n`);
|
|
178
|
-
}
|
|
1
|
+
export { getSupportedOAuthProviders, loginWithOAuthProvider, resolveApiKeyForProvider, } from "../auth.js";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { type AssistantMessage, type ImageContent, type Message, type Model, StringEnum, type UserMessage, } from "@mariozechner/pi-ai";
|
|
2
2
|
export { type Static, type TSchema, Type } from "@sinclair/typebox";
|
|
3
|
+
export { type ApiKeyCredential, type AuthCredential, type AuthMap, getSupportedOAuthProviders, loginWithOAuthProvider, type OAuthCredential, resolveApiKeyForProvider, } from "./auth.js";
|
|
3
4
|
export type { CacheEntry, CacheOptions, CacheStore } from "./cache/index.js";
|
|
4
5
|
export { createFileCache, createMemoryCache, warmCache } from "./cache/index.js";
|
|
5
6
|
export { AbortError, CommandValidationError, ExtractError, HttpValidationError, MaxTurnsError, SchemaValidationError, } from "./errors.js";
|
|
@@ -11,3 +12,4 @@ export { loadRecipeSetup, loadRecipes, resolveRecipe, } from "./recipes/index.js
|
|
|
11
12
|
export type { ExtractionSetup, LoadExtractionSetupOptions } from "./setup.js";
|
|
12
13
|
export { loadExtractionSetup } from "./setup.js";
|
|
13
14
|
export type { ExtractOptions, ExtractResult, ExtractStream, ThinkingBudgets, ThinkingLevel, Usage } from "./types.js";
|
|
15
|
+
export { checkForUpdates, formatUpdateNotification, type UpdateInfo } from "./update.js";
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export { StringEnum, } from "@mariozechner/pi-ai";
|
|
2
2
|
export { Type } from "@sinclair/typebox";
|
|
3
|
+
export { getSupportedOAuthProviders, loginWithOAuthProvider, resolveApiKeyForProvider, } from "./auth.js";
|
|
3
4
|
export { createFileCache, createMemoryCache, warmCache } from "./cache/index.js";
|
|
4
5
|
export { AbortError, CommandValidationError, ExtractError, HttpValidationError, MaxTurnsError, SchemaValidationError, } from "./errors.js";
|
|
5
6
|
export { extract, extractSync } from "./extract.js";
|
|
6
7
|
export { getModel, getModels, getProviders } from "./models.js";
|
|
7
8
|
export { loadRecipeSetup, loadRecipes, resolveRecipe, } from "./recipes/index.js";
|
|
8
9
|
export { loadExtractionSetup } from "./setup.js";
|
|
10
|
+
export { checkForUpdates, formatUpdateNotification } from "./update.js";
|
package/dist/main.js
CHANGED
|
@@ -11,6 +11,7 @@ import { loadExtractionSetupFromContent } from "./core/setup.js";
|
|
|
11
11
|
import { extract } from "./extract.js";
|
|
12
12
|
import { getModels, getProviders } from "./models.js";
|
|
13
13
|
import { loadRecipeSetup, loadRecipes } from "./recipes/index.js";
|
|
14
|
+
import { checkForUpdates, formatUpdateNotification, getVersion } from "./update.js";
|
|
14
15
|
const MODEL_PROVIDER_PREFERENCE = ["anthropic", "openai", "google"];
|
|
15
16
|
class CliExitError extends Error {
|
|
16
17
|
exitCode;
|
|
@@ -121,6 +122,7 @@ async function runCliInternal(argv, deps, stdout, stderr) {
|
|
|
121
122
|
}
|
|
122
123
|
return 0;
|
|
123
124
|
}
|
|
125
|
+
startVersionCheck(stderr);
|
|
124
126
|
const promptInput = args.promptFile ? readTextFile(args.promptFile, "prompt") : args.prompt;
|
|
125
127
|
const promptPath = args.promptFile ? resolve(args.promptFile) : undefined;
|
|
126
128
|
const promptIsSetup = !args.config && !useRecipe && typeof promptInput === "string" && promptInput.startsWith("---");
|
|
@@ -591,11 +593,14 @@ function mapExtractionExitCode(error) {
|
|
|
591
593
|
function writeLine(stream, message) {
|
|
592
594
|
stream.write(`${message}\n`);
|
|
593
595
|
}
|
|
594
|
-
function
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
596
|
+
function startVersionCheck(stderr) {
|
|
597
|
+
void checkForUpdates().then((info) => {
|
|
598
|
+
if (!info)
|
|
599
|
+
return;
|
|
600
|
+
for (const line of formatUpdateNotification(info)) {
|
|
601
|
+
writeLine(stderr, line);
|
|
602
|
+
}
|
|
603
|
+
});
|
|
599
604
|
}
|
|
600
605
|
async function readStream(stream) {
|
|
601
606
|
return await new Promise((resolveStream, reject) => {
|
package/dist/update.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface UpdateInfo {
|
|
2
|
+
packageName: string;
|
|
3
|
+
currentVersion: string;
|
|
4
|
+
latestVersion: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function getVersion(): string;
|
|
7
|
+
export declare function checkForUpdates(options?: {
|
|
8
|
+
skipEnv?: boolean;
|
|
9
|
+
}): Promise<UpdateInfo | undefined>;
|
|
10
|
+
export declare function formatUpdateNotification(info: UpdateInfo): string[];
|
package/dist/update.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
function getPackageInfo() {
|
|
3
|
+
const pkgPath = new URL("../package.json", import.meta.url);
|
|
4
|
+
const raw = readFileSync(pkgPath, "utf8");
|
|
5
|
+
const pkg = JSON.parse(raw);
|
|
6
|
+
return {
|
|
7
|
+
name: pkg.name ?? "pie",
|
|
8
|
+
version: pkg.version ?? "0.0.0",
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export function getVersion() {
|
|
12
|
+
return getPackageInfo().version;
|
|
13
|
+
}
|
|
14
|
+
export async function checkForUpdates(options = {}) {
|
|
15
|
+
if (!options.skipEnv && process.env.PI_SKIP_VERSION_CHECK)
|
|
16
|
+
return undefined;
|
|
17
|
+
const { name, version } = getPackageInfo();
|
|
18
|
+
try {
|
|
19
|
+
const response = await fetch(`https://registry.npmjs.org/${name}/latest`);
|
|
20
|
+
if (!response.ok)
|
|
21
|
+
return undefined;
|
|
22
|
+
const data = (await response.json());
|
|
23
|
+
const latestVersion = data.version;
|
|
24
|
+
if (latestVersion && latestVersion !== version) {
|
|
25
|
+
return {
|
|
26
|
+
packageName: name,
|
|
27
|
+
currentVersion: version,
|
|
28
|
+
latestVersion,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function formatUpdateNotification(info) {
|
|
38
|
+
return [
|
|
39
|
+
`Update available for ${info.packageName}: v${info.latestVersion} (current v${info.currentVersion}). Run: npm install -g ${info.packageName}`,
|
|
40
|
+
"Changelog: https://github.com/justram/pie/blob/main/CHANGELOG.md",
|
|
41
|
+
];
|
|
42
|
+
}
|