orangeslice 2.0.2 → 2.0.3
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 +9 -1
- package/dist/api.js +22 -2
- package/dist/cli.js +81 -58
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,7 +8,15 @@ 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`
|
|
12
20
|
|
|
13
21
|
Install as a dependency when writing app code:
|
|
14
22
|
|
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,73 +114,93 @@ 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
|
-
for (const line of lines) {
|
|
129
|
-
const match = line.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)/);
|
|
130
|
-
if (match)
|
|
131
|
-
entries.set(match[1], match[2]);
|
|
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);
|
|
132
133
|
}
|
|
133
|
-
|
|
134
|
+
catch { }
|
|
134
135
|
}
|
|
135
|
-
function
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const original = fs.readFileSync(envPath, "utf8").split("\n");
|
|
141
|
-
let replaced = false;
|
|
142
|
-
for (const line of original) {
|
|
143
|
-
const match = line.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)/);
|
|
144
|
-
if (match && match[1] === key) {
|
|
145
|
-
lines.push(`${key}=${value}`);
|
|
146
|
-
replaced = true;
|
|
147
|
-
}
|
|
148
|
-
else {
|
|
149
|
-
lines.push(line);
|
|
150
|
-
}
|
|
136
|
+
function openBrowser(url) {
|
|
137
|
+
try {
|
|
138
|
+
if (process.platform === "darwin") {
|
|
139
|
+
(0, child_process_1.execSync)(`open "${url}"`, { stdio: "ignore" });
|
|
140
|
+
return;
|
|
151
141
|
}
|
|
152
|
-
if (
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
lines.push(`${key}=${value}`);
|
|
142
|
+
if (process.platform === "win32") {
|
|
143
|
+
(0, child_process_1.execSync)(`start "" "${url}"`, { stdio: "ignore", shell: "cmd.exe" });
|
|
144
|
+
return;
|
|
156
145
|
}
|
|
146
|
+
(0, child_process_1.execSync)(`xdg-open "${url}"`, { stdio: "ignore" });
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
console.log(` Open this URL in your browser:\n ${url}\n`);
|
|
157
150
|
}
|
|
158
|
-
|
|
159
|
-
|
|
151
|
+
}
|
|
152
|
+
async function startDeviceAuth() {
|
|
153
|
+
const response = await fetch(`${AUTH_API_BASE_URL}/api/orangeslice/auth/start`, {
|
|
154
|
+
method: "POST",
|
|
155
|
+
headers: { "Content-Type": "application/json" }
|
|
156
|
+
});
|
|
157
|
+
const data = (await response.json());
|
|
158
|
+
if (!response.ok || !data.deviceCode || !data.verificationUrl) {
|
|
159
|
+
throw new Error(data.error || "Failed to start device authentication.");
|
|
160
160
|
}
|
|
161
|
-
|
|
161
|
+
return {
|
|
162
|
+
deviceCode: data.deviceCode,
|
|
163
|
+
verificationUrl: data.verificationUrl,
|
|
164
|
+
pollIntervalMs: data.pollIntervalMs && data.pollIntervalMs > 0 ? data.pollIntervalMs : 1000,
|
|
165
|
+
expiresIn: data.expiresIn && data.expiresIn > 0 ? data.expiresIn : 600
|
|
166
|
+
};
|
|
162
167
|
}
|
|
163
|
-
async function
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
168
|
+
async function completeDeviceAuth(deviceCode, pollIntervalMs, expiresIn) {
|
|
169
|
+
const timeoutAt = Date.now() + expiresIn * 1000;
|
|
170
|
+
while (Date.now() < timeoutAt) {
|
|
171
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
172
|
+
const response = await fetch(`${AUTH_API_BASE_URL}/api/orangeslice/auth/poll?deviceCode=${encodeURIComponent(deviceCode)}`, {
|
|
173
|
+
method: "GET",
|
|
174
|
+
headers: { "Content-Type": "application/json" }
|
|
175
|
+
});
|
|
176
|
+
const data = (await response.json());
|
|
177
|
+
if (data.status === "pending")
|
|
178
|
+
continue;
|
|
179
|
+
if (data.status === "approved" && data.apiKey)
|
|
180
|
+
return data.apiKey;
|
|
181
|
+
if (data.status === "expired")
|
|
182
|
+
throw new Error(data.error || "Device code expired.");
|
|
183
|
+
if (data.status === "consumed")
|
|
184
|
+
throw new Error(data.error || "Device code already consumed.");
|
|
185
|
+
if (!response.ok) {
|
|
186
|
+
throw new Error(data.error || "Failed while waiting for authentication.");
|
|
187
|
+
}
|
|
169
188
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
189
|
+
throw new Error("Timed out waiting for browser authentication.");
|
|
190
|
+
}
|
|
191
|
+
async function setupApiKey() {
|
|
192
|
+
const existing = process.env.ORANGESLICE_API_KEY || readConfigFile().apiKey;
|
|
193
|
+
if (existing && existing.trim()) {
|
|
194
|
+
console.log(` ✓ API key already configured (${existing.slice(0, 12)}...)\n`);
|
|
174
195
|
return;
|
|
175
196
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
197
|
+
console.log(" Authenticating with Orange Slice...\n");
|
|
198
|
+
const start = await startDeviceAuth();
|
|
199
|
+
openBrowser(start.verificationUrl);
|
|
200
|
+
console.log(" Browser opened. Complete sign-in there, then return here.\n");
|
|
201
|
+
const apiKey = await completeDeviceAuth(start.deviceCode, start.pollIntervalMs, start.expiresIn);
|
|
202
|
+
saveConfigFile({ apiKey });
|
|
203
|
+
console.log(` ✓ API key saved to ${CONFIG_PATH}\n`);
|
|
181
204
|
}
|
|
182
205
|
async function main() {
|
|
183
206
|
console.log("\norangeslice\n");
|
|
@@ -194,7 +217,7 @@ async function main() {
|
|
|
194
217
|
installOrangeslice(cwd);
|
|
195
218
|
console.log(" ✓ Package installed in current directory\n");
|
|
196
219
|
// API key setup
|
|
197
|
-
await setupApiKey(
|
|
220
|
+
await setupApiKey();
|
|
198
221
|
console.log("\nReady - services-style API\n");
|
|
199
222
|
console.log(" import { configure, services } from 'orangeslice';\n");
|
|
200
223
|
console.log(" // Option A: env var (loaded automatically from .env by your framework)");
|