@implicit-ai/relay 0.0.3 → 0.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/dist/cli.js +100 -21
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -25,24 +25,35 @@ function detectClaudeCodeInstalled() {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
function detectClaudeAuth() {
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
const available = [];
|
|
29
|
+
if (hasOAuthCredentials()) available.push("oauth");
|
|
30
|
+
if (process.env.ANTHROPIC_API_KEY) available.push("api_key");
|
|
31
|
+
const preferApiKey = process.env.IMPLICIT_USE_API_KEY === "1";
|
|
32
|
+
let method;
|
|
33
|
+
if (preferApiKey && available.includes("api_key")) {
|
|
34
|
+
method = "api_key";
|
|
35
|
+
} else if (available.includes("oauth")) {
|
|
36
|
+
method = "oauth";
|
|
37
|
+
} else if (available.includes("api_key")) {
|
|
38
|
+
method = "api_key";
|
|
39
|
+
} else {
|
|
40
|
+
method = "none";
|
|
30
41
|
}
|
|
31
|
-
|
|
32
|
-
|
|
42
|
+
let apiKeyOverridden = false;
|
|
43
|
+
if (method === "oauth" && process.env.ANTHROPIC_API_KEY) {
|
|
44
|
+
delete process.env.ANTHROPIC_API_KEY;
|
|
45
|
+
apiKeyOverridden = true;
|
|
33
46
|
}
|
|
47
|
+
return { method, available, apiKeyOverridden };
|
|
48
|
+
}
|
|
49
|
+
function hasOAuthCredentials() {
|
|
50
|
+
if (process.env.CLAUDE_CODE_OAUTH_TOKEN) return true;
|
|
34
51
|
if (process.platform === "darwin") {
|
|
35
52
|
const keychainServices = ["Claude Code-credentials", "claude.ai"];
|
|
36
53
|
for (const service of keychainServices) {
|
|
37
54
|
try {
|
|
38
|
-
const result = execaSync("security", [
|
|
39
|
-
|
|
40
|
-
"-s",
|
|
41
|
-
service
|
|
42
|
-
]);
|
|
43
|
-
if (result.exitCode === 0) {
|
|
44
|
-
return { method: "oauth" };
|
|
45
|
-
}
|
|
55
|
+
const result = execaSync("security", ["find-generic-password", "-s", service]);
|
|
56
|
+
if (result.exitCode === 0) return true;
|
|
46
57
|
} catch {
|
|
47
58
|
}
|
|
48
59
|
}
|
|
@@ -52,13 +63,11 @@ function detectClaudeAuth() {
|
|
|
52
63
|
if (fs.existsSync(credentialsPath)) {
|
|
53
64
|
const raw = fs.readFileSync(credentialsPath, "utf-8");
|
|
54
65
|
const parsed = JSON.parse(raw);
|
|
55
|
-
if (parsed !== null && typeof parsed === "object")
|
|
56
|
-
return { method: "oauth" };
|
|
57
|
-
}
|
|
66
|
+
if (parsed !== null && typeof parsed === "object") return true;
|
|
58
67
|
}
|
|
59
68
|
} catch {
|
|
60
69
|
}
|
|
61
|
-
return
|
|
70
|
+
return false;
|
|
62
71
|
}
|
|
63
72
|
function printAuthGuidance() {
|
|
64
73
|
console.log("Claude Code auth not found. To set up authentication:");
|
|
@@ -1111,6 +1120,8 @@ var AblyTransport = class {
|
|
|
1111
1120
|
firstConnectDone = false;
|
|
1112
1121
|
/** Guards against re-registering connection state listeners on reconnect. */
|
|
1113
1122
|
connectionListenersWired = false;
|
|
1123
|
+
/** Guards against re-registering channel state listeners across attach attempts. */
|
|
1124
|
+
channelListenersWired = false;
|
|
1114
1125
|
reconnectHandlers = [];
|
|
1115
1126
|
errorHandlers = [];
|
|
1116
1127
|
connectionStateHandlers = [];
|
|
@@ -1146,7 +1157,7 @@ var AblyTransport = class {
|
|
|
1146
1157
|
if (!this.connectionListenersWired) {
|
|
1147
1158
|
this.connectionListenersWired = true;
|
|
1148
1159
|
this.realtime.connection.on("connected", () => {
|
|
1149
|
-
log.info("connection state: connected");
|
|
1160
|
+
log.info({ connectionId: this.realtime?.connection.id, connectionKey: this.realtime?.connection.key }, "connection state: connected");
|
|
1150
1161
|
this._isConnected = true;
|
|
1151
1162
|
if (this.firstConnectDone) {
|
|
1152
1163
|
log.info({ handlers: this.reconnectHandlers.length }, "reconnected \u2014 firing handlers");
|
|
@@ -1181,7 +1192,58 @@ var AblyTransport = class {
|
|
|
1181
1192
|
});
|
|
1182
1193
|
}
|
|
1183
1194
|
this.channel = this.realtime.channels.get(this.channelName);
|
|
1184
|
-
|
|
1195
|
+
if (!this.channelListenersWired) {
|
|
1196
|
+
this.channelListenersWired = true;
|
|
1197
|
+
const ch = this.channel;
|
|
1198
|
+
ch.on("attached", (stateChange) => {
|
|
1199
|
+
log.info(
|
|
1200
|
+
{
|
|
1201
|
+
channel: this.channelName,
|
|
1202
|
+
resumed: stateChange?.resumed,
|
|
1203
|
+
hasBacklog: stateChange?.hasBacklog
|
|
1204
|
+
},
|
|
1205
|
+
"channel state: attached"
|
|
1206
|
+
);
|
|
1207
|
+
});
|
|
1208
|
+
ch.on("failed", (stateChange) => {
|
|
1209
|
+
log.error(
|
|
1210
|
+
{ channel: this.channelName, reason: stateChange?.reason?.message, code: stateChange?.reason?.code },
|
|
1211
|
+
"channel state: failed"
|
|
1212
|
+
);
|
|
1213
|
+
});
|
|
1214
|
+
ch.on("detached", (stateChange) => {
|
|
1215
|
+
log.warn(
|
|
1216
|
+
{ channel: this.channelName, reason: stateChange?.reason?.message, code: stateChange?.reason?.code },
|
|
1217
|
+
"channel state: detached"
|
|
1218
|
+
);
|
|
1219
|
+
});
|
|
1220
|
+
ch.on("suspended", (stateChange) => {
|
|
1221
|
+
log.warn(
|
|
1222
|
+
{ channel: this.channelName, reason: stateChange?.reason?.message, code: stateChange?.reason?.code },
|
|
1223
|
+
"channel state: suspended"
|
|
1224
|
+
);
|
|
1225
|
+
});
|
|
1226
|
+
ch.on("update", (stateChange) => {
|
|
1227
|
+
log.info(
|
|
1228
|
+
{
|
|
1229
|
+
channel: this.channelName,
|
|
1230
|
+
current: stateChange?.current,
|
|
1231
|
+
previous: stateChange?.previous,
|
|
1232
|
+
reason: stateChange?.reason?.message
|
|
1233
|
+
},
|
|
1234
|
+
"channel state: update"
|
|
1235
|
+
);
|
|
1236
|
+
});
|
|
1237
|
+
}
|
|
1238
|
+
log.info({ channel: this.channelName }, "attaching channel");
|
|
1239
|
+
try {
|
|
1240
|
+
await this.channel.attach();
|
|
1241
|
+
log.info({ channel: this.channelName, state: this.channel.state }, "attach() resolved");
|
|
1242
|
+
} catch (err) {
|
|
1243
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1244
|
+
log.error({ channel: this.channelName, err: message }, "attach() rejected");
|
|
1245
|
+
throw err;
|
|
1246
|
+
}
|
|
1185
1247
|
for (const { event, handler } of this.pendingSubscriptions) {
|
|
1186
1248
|
this.channel.subscribe(event, (msg) => {
|
|
1187
1249
|
handler(msg.data);
|
|
@@ -1240,8 +1302,15 @@ var AblyTransport = class {
|
|
|
1240
1302
|
// --- Presence ---
|
|
1241
1303
|
async presenceEnter(data) {
|
|
1242
1304
|
if (!this.channel) throw new Error("AblyTransport: not connected \u2014 call connect() first");
|
|
1243
|
-
log.info({ data }, "entering presence");
|
|
1244
|
-
|
|
1305
|
+
log.info({ data, channelState: this.channel.state }, "entering presence");
|
|
1306
|
+
try {
|
|
1307
|
+
await this.channel.presence.enter(data);
|
|
1308
|
+
log.info({ channel: this.channelName }, "presence.enter() resolved");
|
|
1309
|
+
} catch (err) {
|
|
1310
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1311
|
+
log.error({ channel: this.channelName, err: message }, "presence.enter() rejected");
|
|
1312
|
+
throw err;
|
|
1313
|
+
}
|
|
1245
1314
|
}
|
|
1246
1315
|
async presenceLeave() {
|
|
1247
1316
|
if (!this.channel) return;
|
|
@@ -1324,7 +1393,17 @@ async function connect(options) {
|
|
|
1324
1393
|
printAuthGuidance();
|
|
1325
1394
|
process.exit(1);
|
|
1326
1395
|
}
|
|
1327
|
-
if (!isHeadless)
|
|
1396
|
+
if (!isHeadless) {
|
|
1397
|
+
console.log(` \u2713 Claude auth detected (${claudeAuth.method})`);
|
|
1398
|
+
if (claudeAuth.apiKeyOverridden) {
|
|
1399
|
+
console.log(
|
|
1400
|
+
" \u21B3 ANTHROPIC_API_KEY found but ignored \u2014 using Claude Pro/Max OAuth."
|
|
1401
|
+
);
|
|
1402
|
+
console.log(
|
|
1403
|
+
" Set IMPLICIT_USE_API_KEY=1 to bill via API instead."
|
|
1404
|
+
);
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1328
1407
|
let auth = options.token ? authFromAccessToken(options.token, env) : loadAuth();
|
|
1329
1408
|
if (!auth) {
|
|
1330
1409
|
if (isHeadless) {
|