@papercraneai/cli 1.4.1 → 1.4.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/bin/papercrane.js +20 -24
- package/lib/axios-client.js +14 -0
- package/lib/cloud-client.js +8 -8
- package/lib/config.js +1 -1
- package/lib/environment-client.js +7 -7
- package/lib/facebook-auth.js +4 -4
- package/lib/function-client.js +3 -3
- package/lib/google-auth.js +3 -3
- package/package.json +10 -5
package/bin/papercrane.js
CHANGED
|
@@ -4,19 +4,12 @@ import { Command } from 'commander';
|
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import readline from 'readline';
|
|
6
6
|
import fs from 'fs/promises';
|
|
7
|
-
import {
|
|
7
|
+
import { http } from '../lib/axios-client.js';
|
|
8
8
|
import { setApiKey, clearConfig, isLoggedIn, setDefaultWorkspace, getDefaultWorkspace } from '../lib/config.js';
|
|
9
9
|
import { validateApiKey } from '../lib/cloud-client.js';
|
|
10
10
|
import { listFunctions, getFunction, runFunction, formatDescribe, formatDescribeRoot, formatFlat, formatResult, formatUnconnected } from '../lib/function-client.js';
|
|
11
11
|
import { listWorkspaces, resolveWorkspaceId, getFileTree, readFile, writeFile, editFile, deleteFile, getLocalWorkspacePath, pullWorkspace, pushWorkspace } from '../lib/environment-client.js';
|
|
12
12
|
|
|
13
|
-
// Configure proxy support for environments like Claude's container
|
|
14
|
-
// Node.js native fetch() doesn't respect HTTP_PROXY/HTTPS_PROXY env vars
|
|
15
|
-
const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
|
|
16
|
-
if (proxyUrl) {
|
|
17
|
-
setGlobalDispatcher(new ProxyAgent(proxyUrl));
|
|
18
|
-
}
|
|
19
|
-
|
|
20
13
|
const program = new Command();
|
|
21
14
|
|
|
22
15
|
program
|
|
@@ -29,7 +22,7 @@ program
|
|
|
29
22
|
.description('Login to Papercrane. Opens browser for authentication, or use --api-key for direct login.')
|
|
30
23
|
.option('--api-key <key>', 'API key for direct login (skips browser)')
|
|
31
24
|
.option('--url <url>', 'API base URL (saves to config)')
|
|
32
|
-
.option('--
|
|
25
|
+
.option('--nowait', 'Print auth URL and exit immediately (for AI assistants)')
|
|
33
26
|
.option('--check', 'Check if pending login was completed')
|
|
34
27
|
.action(async (options) => {
|
|
35
28
|
try {
|
|
@@ -65,14 +58,16 @@ program
|
|
|
65
58
|
if (options.check) {
|
|
66
59
|
const pending = await getPendingSession();
|
|
67
60
|
if (!pending) {
|
|
68
|
-
console.log(chalk.yellow('No pending login session. Run: papercrane login --
|
|
61
|
+
console.log(chalk.yellow('No pending login session. Run: papercrane login --nowait'));
|
|
69
62
|
process.exit(1);
|
|
70
63
|
}
|
|
71
64
|
|
|
72
|
-
const statusRes = await
|
|
65
|
+
const statusRes = await http.get(`${pending.baseUrl}/api/cli-auth/status?session=${encodeURIComponent(pending.session)}`, {
|
|
66
|
+
validateStatus: () => true
|
|
67
|
+
});
|
|
73
68
|
|
|
74
|
-
if (statusRes.
|
|
75
|
-
const data =
|
|
69
|
+
if (statusRes.status >= 200 && statusRes.status < 300) {
|
|
70
|
+
const data = statusRes.data;
|
|
76
71
|
if (data.status === 'authorized') {
|
|
77
72
|
await setApiKey(data.key);
|
|
78
73
|
await setApiBaseUrl(pending.baseUrl);
|
|
@@ -93,25 +88,24 @@ program
|
|
|
93
88
|
const session = generateState();
|
|
94
89
|
|
|
95
90
|
// Initialize session on server
|
|
96
|
-
const initRes = await
|
|
97
|
-
method: 'POST',
|
|
91
|
+
const initRes = await http.post(`${baseUrl}/api/cli-auth/init`, { session }, {
|
|
98
92
|
headers: { 'Content-Type': 'application/json' },
|
|
99
|
-
|
|
93
|
+
validateStatus: () => true
|
|
100
94
|
});
|
|
101
95
|
|
|
102
|
-
if (
|
|
96
|
+
if (initRes.status < 200 || initRes.status >= 300) {
|
|
103
97
|
throw new Error('Failed to initialize login session');
|
|
104
98
|
}
|
|
105
99
|
|
|
106
100
|
const authUrl = `${baseUrl}/cli-auth?session=${session}`;
|
|
107
101
|
|
|
108
|
-
// --
|
|
109
|
-
if (options.
|
|
102
|
+
// --nowait: Print URL and exit immediately (for AI assistants)
|
|
103
|
+
if (options.nowait) {
|
|
110
104
|
await setPendingSession({ session, baseUrl });
|
|
111
105
|
console.log(chalk.cyan('\nOpen this URL to authenticate:\n'));
|
|
112
106
|
console.log(` ${authUrl}\n`);
|
|
113
107
|
console.log(chalk.dim('After authorizing, run: papercrane login --check\n'));
|
|
114
|
-
|
|
108
|
+
process.exit(0);
|
|
115
109
|
}
|
|
116
110
|
|
|
117
111
|
console.log(chalk.cyan('\nOpen this URL to authenticate:\n'));
|
|
@@ -134,10 +128,12 @@ program
|
|
|
134
128
|
const startTime = Date.now();
|
|
135
129
|
|
|
136
130
|
while (Date.now() - startTime < timeout) {
|
|
137
|
-
const statusRes = await
|
|
131
|
+
const statusRes = await http.get(`${baseUrl}/api/cli-auth/status?session=${encodeURIComponent(session)}`, {
|
|
132
|
+
validateStatus: () => true
|
|
133
|
+
});
|
|
138
134
|
|
|
139
|
-
if (statusRes.
|
|
140
|
-
const data =
|
|
135
|
+
if (statusRes.status >= 200 && statusRes.status < 300) {
|
|
136
|
+
const data = statusRes.data;
|
|
141
137
|
|
|
142
138
|
if (data.status === 'authorized') {
|
|
143
139
|
// Save the API key
|
|
@@ -840,4 +836,4 @@ program.action(async (_, cmd) => {
|
|
|
840
836
|
}
|
|
841
837
|
});
|
|
842
838
|
|
|
843
|
-
program.parse();
|
|
839
|
+
program.parse();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import axios from "axios"
|
|
2
|
+
import { HttpsProxyAgent } from "https-proxy-agent"
|
|
3
|
+
|
|
4
|
+
const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY
|
|
5
|
+
|
|
6
|
+
const axiosConfig = proxyUrl
|
|
7
|
+
? {
|
|
8
|
+
httpAgent: new HttpsProxyAgent(proxyUrl),
|
|
9
|
+
httpsAgent: new HttpsProxyAgent(proxyUrl),
|
|
10
|
+
proxy: false
|
|
11
|
+
}
|
|
12
|
+
: {}
|
|
13
|
+
|
|
14
|
+
export const http = axios.create(axiosConfig)
|
package/lib/cloud-client.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { http } from './axios-client.js';
|
|
2
2
|
import { getApiKey, getApiBaseUrl } from './config.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -16,7 +16,7 @@ export async function fetchCloudCredentials(provider, instanceName = 'Default')
|
|
|
16
16
|
const baseUrl = await getApiBaseUrl();
|
|
17
17
|
|
|
18
18
|
try {
|
|
19
|
-
const response = await
|
|
19
|
+
const response = await http.get(`${baseUrl}/api/sdk/credentials/${provider}/${instanceName}`, {
|
|
20
20
|
headers: {
|
|
21
21
|
'Authorization': `Bearer ${apiKey}`
|
|
22
22
|
}
|
|
@@ -52,7 +52,7 @@ export async function listCloudCredentials() {
|
|
|
52
52
|
const baseUrl = await getApiBaseUrl();
|
|
53
53
|
|
|
54
54
|
try {
|
|
55
|
-
const response = await
|
|
55
|
+
const response = await http.get(`${baseUrl}/api/sdk/credentials`, {
|
|
56
56
|
headers: {
|
|
57
57
|
'Authorization': `Bearer ${apiKey}`
|
|
58
58
|
}
|
|
@@ -84,7 +84,7 @@ export async function validateApiKey() {
|
|
|
84
84
|
// We expect either a 404 (integration not found, but key is valid)
|
|
85
85
|
// or a 200 (key is valid and integration exists)
|
|
86
86
|
// A 401 means the key is invalid
|
|
87
|
-
await
|
|
87
|
+
await http.get(`${baseUrl}/api/sdk/credentials/google/Default`, {
|
|
88
88
|
headers: {
|
|
89
89
|
'Authorization': `Bearer ${apiKey}`
|
|
90
90
|
}
|
|
@@ -120,7 +120,7 @@ export async function refreshCloudCredentials(provider, instanceName = 'Default'
|
|
|
120
120
|
const baseUrl = await getApiBaseUrl();
|
|
121
121
|
|
|
122
122
|
try {
|
|
123
|
-
const response = await
|
|
123
|
+
const response = await http.post(`${baseUrl}/api/sdk/credentials/${provider}/${instanceName}/refresh`, {}, {
|
|
124
124
|
headers: {
|
|
125
125
|
'Authorization': `Bearer ${apiKey}`
|
|
126
126
|
}
|
|
@@ -155,7 +155,7 @@ export async function pushCredentials(provider, credentialId, credentials, scope
|
|
|
155
155
|
|
|
156
156
|
const baseUrl = await getApiBaseUrl();
|
|
157
157
|
|
|
158
|
-
const response = await
|
|
158
|
+
const response = await http.post(`${baseUrl}/api/sdk/credentials/push`, {
|
|
159
159
|
provider,
|
|
160
160
|
credential_id: credentialId,
|
|
161
161
|
credentials,
|
|
@@ -183,11 +183,11 @@ export async function pullCredentials() {
|
|
|
183
183
|
|
|
184
184
|
const baseUrl = await getApiBaseUrl();
|
|
185
185
|
|
|
186
|
-
const response = await
|
|
186
|
+
const response = await http.get(`${baseUrl}/api/sdk/credentials/pull`, {
|
|
187
187
|
headers: {
|
|
188
188
|
'Authorization': `Bearer ${apiKey}`
|
|
189
189
|
}
|
|
190
190
|
});
|
|
191
191
|
|
|
192
192
|
return response.data.credentials || [];
|
|
193
|
-
}
|
|
193
|
+
}
|
package/lib/config.js
CHANGED
|
@@ -125,7 +125,7 @@ export async function clearDefaultWorkspace() {
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
/**
|
|
128
|
-
* Get pending login session (for --
|
|
128
|
+
* Get pending login session (for --nowait / --check flow)
|
|
129
129
|
* @returns {Promise<{session: string, baseUrl: string}|null>}
|
|
130
130
|
*/
|
|
131
131
|
export async function getPendingSession() {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { http } from './axios-client.js';
|
|
2
2
|
import { homedir } from 'os';
|
|
3
3
|
import { join } from 'path';
|
|
4
4
|
import { getApiKey, getApiBaseUrl, getDefaultWorkspace, setDefaultWorkspace } from './config.js';
|
|
@@ -62,7 +62,7 @@ export async function listWorkspaces() {
|
|
|
62
62
|
const headers = await getAuthHeaders();
|
|
63
63
|
const baseUrl = await getApiBaseUrl();
|
|
64
64
|
|
|
65
|
-
const response = await
|
|
65
|
+
const response = await http.get(`${baseUrl}/sdk/workspaces`, { headers });
|
|
66
66
|
return response.data.workspaces;
|
|
67
67
|
}
|
|
68
68
|
|
|
@@ -80,7 +80,7 @@ export async function getFileTree(workspaceId, path = '') {
|
|
|
80
80
|
? `${baseUrl}/sdk/workspaces/${workspaceId}/files?path=${encodeURIComponent(path)}`
|
|
81
81
|
: `${baseUrl}/sdk/workspaces/${workspaceId}/files`;
|
|
82
82
|
|
|
83
|
-
const response = await
|
|
83
|
+
const response = await http.get(url, { headers });
|
|
84
84
|
return response.data;
|
|
85
85
|
}
|
|
86
86
|
|
|
@@ -94,7 +94,7 @@ export async function readFile(workspaceId, path) {
|
|
|
94
94
|
const headers = await getAuthHeaders();
|
|
95
95
|
const baseUrl = await getApiBaseUrl();
|
|
96
96
|
|
|
97
|
-
const response = await
|
|
97
|
+
const response = await http.get(
|
|
98
98
|
`${baseUrl}/sdk/workspaces/${workspaceId}/files/read?path=${encodeURIComponent(path)}`,
|
|
99
99
|
{ headers }
|
|
100
100
|
);
|
|
@@ -112,7 +112,7 @@ export async function writeFile(workspaceId, path, content) {
|
|
|
112
112
|
const headers = await getAuthHeaders();
|
|
113
113
|
const baseUrl = await getApiBaseUrl();
|
|
114
114
|
|
|
115
|
-
const response = await
|
|
115
|
+
const response = await http.post(
|
|
116
116
|
`${baseUrl}/sdk/workspaces/${workspaceId}/files/write`,
|
|
117
117
|
{ path, content },
|
|
118
118
|
{ headers }
|
|
@@ -133,7 +133,7 @@ export async function editFile(workspaceId, path, oldString, newString, replaceA
|
|
|
133
133
|
const headers = await getAuthHeaders();
|
|
134
134
|
const baseUrl = await getApiBaseUrl();
|
|
135
135
|
|
|
136
|
-
const response = await
|
|
136
|
+
const response = await http.post(
|
|
137
137
|
`${baseUrl}/sdk/workspaces/${workspaceId}/files/edit`,
|
|
138
138
|
{ path, old_string: oldString, new_string: newString, replace_all: replaceAll },
|
|
139
139
|
{ headers }
|
|
@@ -151,7 +151,7 @@ export async function deleteFile(workspaceId, path) {
|
|
|
151
151
|
const headers = await getAuthHeaders();
|
|
152
152
|
const baseUrl = await getApiBaseUrl();
|
|
153
153
|
|
|
154
|
-
const response = await
|
|
154
|
+
const response = await http.delete(
|
|
155
155
|
`${baseUrl}/sdk/workspaces/${workspaceId}/files?path=${encodeURIComponent(path)}`,
|
|
156
156
|
{ headers }
|
|
157
157
|
);
|
package/lib/facebook-auth.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { http } from './axios-client.js';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { saveFacebookCredentials } from './storage.js';
|
|
4
4
|
|
|
@@ -46,7 +46,7 @@ export async function handleFacebookAuth(scopes, appId, clientToken) {
|
|
|
46
46
|
|
|
47
47
|
// Step 1: Request device code
|
|
48
48
|
console.log(chalk.cyan('📱 Requesting device code...'));
|
|
49
|
-
const deviceResponse = await
|
|
49
|
+
const deviceResponse = await http.post(DEVICE_LOGIN_URL, null, {
|
|
50
50
|
params: {
|
|
51
51
|
access_token: accessToken,
|
|
52
52
|
scope: scopeString
|
|
@@ -80,7 +80,7 @@ export async function handleFacebookAuth(scopes, appId, clientToken) {
|
|
|
80
80
|
await new Promise(resolve => setTimeout(resolve, pollInterval));
|
|
81
81
|
|
|
82
82
|
try {
|
|
83
|
-
const statusResponse = await
|
|
83
|
+
const statusResponse = await http.post(DEVICE_STATUS_URL, null, {
|
|
84
84
|
params: {
|
|
85
85
|
access_token: accessToken,
|
|
86
86
|
code: code
|
|
@@ -145,4 +145,4 @@ export async function handleFacebookAuth(scopes, appId, clientToken) {
|
|
|
145
145
|
throw error;
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
|
-
}
|
|
148
|
+
}
|
package/lib/function-client.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { http } from "./axios-client.js"
|
|
2
2
|
import chalk from "chalk"
|
|
3
3
|
import { getApiKey, getApiBaseUrl } from "./config.js"
|
|
4
4
|
|
|
@@ -55,7 +55,7 @@ async function functionRequest(
|
|
|
55
55
|
|
|
56
56
|
// For POST requests, use streaming to handle large/streaming responses
|
|
57
57
|
if (method === "POST") {
|
|
58
|
-
const response = await
|
|
58
|
+
const response = await http({
|
|
59
59
|
method,
|
|
60
60
|
url,
|
|
61
61
|
headers: {
|
|
@@ -125,7 +125,7 @@ async function functionRequest(
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
// GET requests - use regular axios behavior
|
|
128
|
-
const response = await
|
|
128
|
+
const response = await http({
|
|
129
129
|
method,
|
|
130
130
|
url,
|
|
131
131
|
headers: {
|
package/lib/google-auth.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { http } from './axios-client.js';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import open from 'open';
|
|
4
4
|
import { generatePKCE } from './pkce.js';
|
|
@@ -88,7 +88,7 @@ export async function handleGoogleAuth(scopes, clientId, clientSecret) {
|
|
|
88
88
|
// Exchange authorization code for tokens
|
|
89
89
|
console.log(chalk.cyan('🔄 Exchanging authorization code for tokens...'));
|
|
90
90
|
|
|
91
|
-
const tokenResponse = await
|
|
91
|
+
const tokenResponse = await http.post(
|
|
92
92
|
GOOGLE_TOKEN_URL,
|
|
93
93
|
new URLSearchParams({
|
|
94
94
|
code,
|
|
@@ -131,4 +131,4 @@ export async function handleGoogleAuth(scopes, clientId, clientSecret) {
|
|
|
131
131
|
throw error;
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
|
-
}
|
|
134
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@papercraneai/cli",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.4",
|
|
4
4
|
"description": "CLI tool for managing OAuth credentials for LLM integrations",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -17,15 +17,20 @@
|
|
|
17
17
|
"scripts": {
|
|
18
18
|
"start": "node bin/papercrane.js"
|
|
19
19
|
},
|
|
20
|
-
"keywords": [
|
|
20
|
+
"keywords": [
|
|
21
|
+
"oauth",
|
|
22
|
+
"cli",
|
|
23
|
+
"authentication",
|
|
24
|
+
"google"
|
|
25
|
+
],
|
|
21
26
|
"author": "",
|
|
22
27
|
"license": "MIT",
|
|
23
28
|
"dependencies": {
|
|
24
29
|
"commander": "^12.0.0",
|
|
25
30
|
"axios": "^1.6.0",
|
|
26
31
|
"chalk": "^4.1.2",
|
|
32
|
+
"https-proxy-agent": "^7.0.4",
|
|
27
33
|
"inquirer": "^8.2.6",
|
|
28
|
-
"open": "^8.4.2"
|
|
29
|
-
"undici": "^6.0.0"
|
|
34
|
+
"open": "^8.4.2"
|
|
30
35
|
}
|
|
31
|
-
}
|
|
36
|
+
}
|