orangeslice 2.0.2 → 2.0.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/README.md +25 -1
- package/dist/api.js +22 -2
- package/dist/cli.js +158 -58
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,7 +8,31 @@ Orangeslice provides a `services.*` API for B2B research, enrichment, scraping,
|
|
|
8
8
|
npx orangeslice
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
The CLI copies docs to `./orangeslice-docs`, creates `./orangeslice-docs/AGENTS.md`, initializes `package.json` when missing,
|
|
11
|
+
The CLI copies docs to `./orangeslice-docs`, creates `./orangeslice-docs/AGENTS.md`, initializes `package.json` when missing, installs `orangeslice` in the current directory, opens browser auth, and stores your API key in `~/.config/orangeslice/config.json`.
|
|
12
|
+
|
|
13
|
+
### Auth behavior
|
|
14
|
+
|
|
15
|
+
- `npx orangeslice` uses browser-based device auth and auto-provisions an API key.
|
|
16
|
+
- SDK credential precedence:
|
|
17
|
+
1. `configure({ apiKey })`
|
|
18
|
+
2. `ORANGESLICE_API_KEY` env var
|
|
19
|
+
3. `~/.config/orangeslice/config.json`
|
|
20
|
+
|
|
21
|
+
### CLI auth commands
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# One-time setup (also runs on plain `npx orangeslice` when unauthenticated)
|
|
25
|
+
npx orangeslice login
|
|
26
|
+
|
|
27
|
+
# Force re-auth and replace local stored key
|
|
28
|
+
npx orangeslice login --force
|
|
29
|
+
|
|
30
|
+
# Remove locally stored key
|
|
31
|
+
npx orangeslice logout
|
|
32
|
+
|
|
33
|
+
# Show current auth source (env or config file)
|
|
34
|
+
npx orangeslice auth status
|
|
35
|
+
```
|
|
12
36
|
|
|
13
37
|
Install as a dependency when writing app code:
|
|
14
38
|
|
package/dist/api.js
CHANGED
|
@@ -11,6 +11,7 @@ const DEFAULT_BASE_URL = "https://enrichly-production.up.railway.app";
|
|
|
11
11
|
const POLL_TIMEOUT_MS = 600000;
|
|
12
12
|
const DEFAULT_POLL_INTERVAL_MS = 1000;
|
|
13
13
|
const DEFAULT_INLINE_WAIT_MS = 5000;
|
|
14
|
+
const USER_CONFIG_PATH = ".config/orangeslice/config.json";
|
|
14
15
|
const _config = {};
|
|
15
16
|
function configure(opts) {
|
|
16
17
|
if (opts.apiKey !== undefined)
|
|
@@ -22,7 +23,26 @@ function resolveBaseUrl() {
|
|
|
22
23
|
return (_config.baseUrl || process.env.ORANGESLICE_BASE_URL || DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
23
24
|
}
|
|
24
25
|
function resolveApiKey() {
|
|
25
|
-
return _config.apiKey || process.env.ORANGESLICE_API_KEY || "";
|
|
26
|
+
return _config.apiKey || process.env.ORANGESLICE_API_KEY || readApiKeyFromConfigFile() || "";
|
|
27
|
+
}
|
|
28
|
+
function readApiKeyFromConfigFile() {
|
|
29
|
+
// Guard for browser-like runtimes.
|
|
30
|
+
if (!process?.versions?.node)
|
|
31
|
+
return "";
|
|
32
|
+
try {
|
|
33
|
+
const os = require("os");
|
|
34
|
+
const fs = require("fs");
|
|
35
|
+
const path = require("path");
|
|
36
|
+
const configPath = path.join(os.homedir(), USER_CONFIG_PATH);
|
|
37
|
+
if (!fs.existsSync(configPath))
|
|
38
|
+
return "";
|
|
39
|
+
const raw = fs.readFileSync(configPath, "utf8");
|
|
40
|
+
const parsed = JSON.parse(raw);
|
|
41
|
+
return typeof parsed.apiKey === "string" ? parsed.apiKey : "";
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return "";
|
|
45
|
+
}
|
|
26
46
|
}
|
|
27
47
|
function sleep(ms) {
|
|
28
48
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -117,7 +137,7 @@ async function post(endpoint, payload) {
|
|
|
117
137
|
const apiKey = resolveApiKey();
|
|
118
138
|
if (!apiKey) {
|
|
119
139
|
throw new Error("[orangeslice] No API key configured. " +
|
|
120
|
-
"
|
|
140
|
+
"Run `npx orangeslice`, set ORANGESLICE_API_KEY in your environment, or call configure({ apiKey: 'osk_...' }).");
|
|
121
141
|
}
|
|
122
142
|
const url = `${baseUrl}${endpoint}`;
|
|
123
143
|
const body = JSON.stringify({ ...payload, inlineWaitMs: DEFAULT_INLINE_WAIT_MS });
|
package/dist/cli.js
CHANGED
|
@@ -36,11 +36,14 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
36
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
37
|
const child_process_1 = require("child_process");
|
|
38
38
|
const fs = __importStar(require("fs"));
|
|
39
|
+
const os = __importStar(require("os"));
|
|
39
40
|
const path = __importStar(require("path"));
|
|
40
|
-
const readline = __importStar(require("readline"));
|
|
41
41
|
const LEGACY_DOCS_DIR = path.join(__dirname, "..", "docs");
|
|
42
42
|
const TARGET_DIR = path.join(process.cwd(), "orangeslice-docs");
|
|
43
43
|
const AGENTS_FILE = path.join(TARGET_DIR, "AGENTS.md");
|
|
44
|
+
const AUTH_API_BASE_URL = "https://www.orangeslice.ai";
|
|
45
|
+
const CONFIG_DIR = path.join(os.homedir(), ".config", "orangeslice");
|
|
46
|
+
const CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
|
|
44
47
|
function isDir(p) {
|
|
45
48
|
try {
|
|
46
49
|
return fs.statSync(p).isDirectory();
|
|
@@ -111,75 +114,172 @@ function installOrangeslice(cwd) {
|
|
|
111
114
|
console.log(" Installing orangeslice...");
|
|
112
115
|
(0, child_process_1.execSync)("npm install orangeslice", { stdio: "inherit", cwd });
|
|
113
116
|
}
|
|
114
|
-
function
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
117
|
+
function readConfigFile() {
|
|
118
|
+
try {
|
|
119
|
+
if (!fs.existsSync(CONFIG_PATH))
|
|
120
|
+
return {};
|
|
121
|
+
const parsed = JSON.parse(fs.readFileSync(CONFIG_PATH, "utf8"));
|
|
122
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return {};
|
|
126
|
+
}
|
|
122
127
|
}
|
|
123
|
-
function
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
128
|
+
function saveConfigFile(config) {
|
|
129
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
130
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", { encoding: "utf8", mode: 0o600 });
|
|
131
|
+
try {
|
|
132
|
+
fs.chmodSync(CONFIG_PATH, 0o600);
|
|
133
|
+
}
|
|
134
|
+
catch { }
|
|
135
|
+
}
|
|
136
|
+
function clearConfigFile() {
|
|
137
|
+
try {
|
|
138
|
+
if (!fs.existsSync(CONFIG_PATH))
|
|
139
|
+
return false;
|
|
140
|
+
fs.unlinkSync(CONFIG_PATH);
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function maskKey(value) {
|
|
148
|
+
if (value.length <= 12)
|
|
149
|
+
return value;
|
|
150
|
+
return `${value.slice(0, 8)}...${value.slice(-4)}`;
|
|
151
|
+
}
|
|
152
|
+
function getStoredApiKey() {
|
|
153
|
+
const envKey = process.env.ORANGESLICE_API_KEY;
|
|
154
|
+
if (envKey && envKey.trim())
|
|
155
|
+
return envKey.trim();
|
|
156
|
+
const fileKey = readConfigFile().apiKey;
|
|
157
|
+
return fileKey?.trim() || "";
|
|
158
|
+
}
|
|
159
|
+
function openBrowser(url) {
|
|
160
|
+
try {
|
|
161
|
+
if (process.platform === "darwin") {
|
|
162
|
+
(0, child_process_1.execSync)(`open "${url}"`, { stdio: "ignore" });
|
|
163
|
+
return;
|
|
151
164
|
}
|
|
152
|
-
if (
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
lines.push(`${key}=${value}`);
|
|
165
|
+
if (process.platform === "win32") {
|
|
166
|
+
(0, child_process_1.execSync)(`start "" "${url}"`, { stdio: "ignore", shell: "cmd.exe" });
|
|
167
|
+
return;
|
|
156
168
|
}
|
|
169
|
+
(0, child_process_1.execSync)(`xdg-open "${url}"`, { stdio: "ignore" });
|
|
157
170
|
}
|
|
158
|
-
|
|
159
|
-
|
|
171
|
+
catch {
|
|
172
|
+
console.log(` Open this URL in your browser:\n ${url}\n`);
|
|
160
173
|
}
|
|
161
|
-
fs.writeFileSync(envPath, lines.join("\n"), "utf8");
|
|
162
174
|
}
|
|
163
|
-
async function
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
175
|
+
async function startDeviceAuth() {
|
|
176
|
+
const response = await fetch(`${AUTH_API_BASE_URL}/api/orangeslice/auth/start`, {
|
|
177
|
+
method: "POST",
|
|
178
|
+
headers: { "Content-Type": "application/json" }
|
|
179
|
+
});
|
|
180
|
+
const data = (await response.json());
|
|
181
|
+
if (!response.ok || !data.deviceCode || !data.verificationUrl) {
|
|
182
|
+
throw new Error(data.error || "Failed to start device authentication.");
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
deviceCode: data.deviceCode,
|
|
186
|
+
verificationUrl: data.verificationUrl,
|
|
187
|
+
pollIntervalMs: data.pollIntervalMs && data.pollIntervalMs > 0 ? data.pollIntervalMs : 1000,
|
|
188
|
+
expiresIn: data.expiresIn && data.expiresIn > 0 ? data.expiresIn : 600
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
async function completeDeviceAuth(deviceCode, pollIntervalMs, expiresIn) {
|
|
192
|
+
const timeoutAt = Date.now() + expiresIn * 1000;
|
|
193
|
+
while (Date.now() < timeoutAt) {
|
|
194
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
195
|
+
const response = await fetch(`${AUTH_API_BASE_URL}/api/orangeslice/auth/poll?deviceCode=${encodeURIComponent(deviceCode)}`, {
|
|
196
|
+
method: "GET",
|
|
197
|
+
headers: { "Content-Type": "application/json" }
|
|
198
|
+
});
|
|
199
|
+
const data = (await response.json());
|
|
200
|
+
if (data.status === "pending")
|
|
201
|
+
continue;
|
|
202
|
+
if (data.status === "approved" && data.apiKey)
|
|
203
|
+
return data.apiKey;
|
|
204
|
+
if (data.status === "expired")
|
|
205
|
+
throw new Error(data.error || "Device code expired.");
|
|
206
|
+
if (data.status === "consumed")
|
|
207
|
+
throw new Error(data.error || "Device code already consumed.");
|
|
208
|
+
if (!response.ok) {
|
|
209
|
+
throw new Error(data.error || "Failed while waiting for authentication.");
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
throw new Error("Timed out waiting for browser authentication.");
|
|
213
|
+
}
|
|
214
|
+
async function setupApiKey(force = false) {
|
|
215
|
+
const existing = getStoredApiKey();
|
|
216
|
+
if (!force && existing) {
|
|
217
|
+
console.log(` ✓ API key already configured (${maskKey(existing)})\n`);
|
|
168
218
|
return;
|
|
169
219
|
}
|
|
170
|
-
console.log("
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
220
|
+
console.log(" Authenticating with Orange Slice...\n");
|
|
221
|
+
const start = await startDeviceAuth();
|
|
222
|
+
openBrowser(start.verificationUrl);
|
|
223
|
+
console.log(" Browser opened. Complete sign-in there, then return here.\n");
|
|
224
|
+
const apiKey = await completeDeviceAuth(start.deviceCode, start.pollIntervalMs, start.expiresIn);
|
|
225
|
+
saveConfigFile({ apiKey });
|
|
226
|
+
console.log(` ✓ API key saved to ${CONFIG_PATH}\n`);
|
|
227
|
+
}
|
|
228
|
+
function printHelp() {
|
|
229
|
+
console.log("Usage:");
|
|
230
|
+
console.log(" npx orangeslice");
|
|
231
|
+
console.log(" npx orangeslice login [--force]");
|
|
232
|
+
console.log(" npx orangeslice logout");
|
|
233
|
+
console.log(" npx orangeslice auth status\n");
|
|
234
|
+
}
|
|
235
|
+
async function runLogin(args) {
|
|
236
|
+
const force = args.includes("--force");
|
|
237
|
+
await setupApiKey(force);
|
|
238
|
+
}
|
|
239
|
+
function runLogout() {
|
|
240
|
+
const removed = clearConfigFile();
|
|
241
|
+
if (removed) {
|
|
242
|
+
console.log(` ✓ Removed local auth at ${CONFIG_PATH}`);
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
console.log(" No local config found to remove.");
|
|
246
|
+
}
|
|
247
|
+
if (process.env.ORANGESLICE_API_KEY) {
|
|
248
|
+
console.log(" Note: ORANGESLICE_API_KEY env var is still set in this shell.");
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function runAuthStatus() {
|
|
252
|
+
const envKey = process.env.ORANGESLICE_API_KEY?.trim() || "";
|
|
253
|
+
if (envKey) {
|
|
254
|
+
console.log(` Authenticated via env var: ${maskKey(envKey)}`);
|
|
174
255
|
return;
|
|
175
256
|
}
|
|
176
|
-
|
|
177
|
-
|
|
257
|
+
const fileKey = readConfigFile().apiKey?.trim() || "";
|
|
258
|
+
if (fileKey) {
|
|
259
|
+
console.log(` Authenticated via config file (${CONFIG_PATH}): ${maskKey(fileKey)}`);
|
|
260
|
+
return;
|
|
178
261
|
}
|
|
179
|
-
|
|
180
|
-
console.log(`\n ✓ API key saved to .env\n`);
|
|
262
|
+
console.log(" Not authenticated. Run `npx orangeslice login`.");
|
|
181
263
|
}
|
|
182
264
|
async function main() {
|
|
265
|
+
const args = process.argv.slice(2);
|
|
266
|
+
const command = args[0];
|
|
267
|
+
if (command === "login") {
|
|
268
|
+
await runLogin(args.slice(1));
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
if (command === "logout") {
|
|
272
|
+
runLogout();
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
if (command === "auth" && args[1] === "status") {
|
|
276
|
+
runAuthStatus();
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
if (command && (command === "--help" || command === "-h" || command === "help")) {
|
|
280
|
+
printHelp();
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
183
283
|
console.log("\norangeslice\n");
|
|
184
284
|
const docsDir = resolveDocsDir();
|
|
185
285
|
// Copy docs
|
|
@@ -194,7 +294,7 @@ async function main() {
|
|
|
194
294
|
installOrangeslice(cwd);
|
|
195
295
|
console.log(" ✓ Package installed in current directory\n");
|
|
196
296
|
// API key setup
|
|
197
|
-
await setupApiKey(
|
|
297
|
+
await setupApiKey();
|
|
198
298
|
console.log("\nReady - services-style API\n");
|
|
199
299
|
console.log(" import { configure, services } from 'orangeslice';\n");
|
|
200
300
|
console.log(" // Option A: env var (loaded automatically from .env by your framework)");
|