clay-server 4.0.0-beta.6 → 4.0.0-beta.8
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/lib/project-vendor-login.js +20 -3
- package/lib/project.js +2 -0
- package/lib/sdk-message-processor.js +16 -2
- package/lib/server.js +19 -0
- package/lib/yoke/adapters/codex.js +17 -0
- package/package.json +1 -1
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
// auth_required forever.
|
|
21
21
|
|
|
22
22
|
var vendorRegistry = require("./yoke/vendor-registry");
|
|
23
|
+
var yoke = require("./yoke");
|
|
23
24
|
|
|
24
25
|
// Login CLIs print a success line and then hand the shell back, so the PTY
|
|
25
26
|
// itself does not exit. Watch the output stream instead. Deliberately narrow:
|
|
@@ -62,7 +63,8 @@ function looksLikeLoginSuccess(text) {
|
|
|
62
63
|
* sm, tm, adapters,
|
|
63
64
|
* send, sendTo,
|
|
64
65
|
* usersModule,
|
|
65
|
-
* getOsUserInfoForWs, getOsUserInfoForLinuxUser, getLinuxUserForSession
|
|
66
|
+
* getOsUserInfoForWs, getOsUserInfoForLinuxUser, getLinuxUserForSession,
|
|
67
|
+
* refreshVendorAuthEverywhere
|
|
66
68
|
*/
|
|
67
69
|
function attachVendorLogin(ctx) {
|
|
68
70
|
var slug = ctx.slug;
|
|
@@ -80,6 +82,7 @@ function attachVendorLogin(ctx) {
|
|
|
80
82
|
var getOsUserInfoForWs = ctx.getOsUserInfoForWs;
|
|
81
83
|
var getOsUserInfoForLinuxUser = ctx.getOsUserInfoForLinuxUser;
|
|
82
84
|
var getLinuxUserForSession = ctx.getLinuxUserForSession;
|
|
85
|
+
var refreshVendorAuthEverywhere = ctx.refreshVendorAuthEverywhere;
|
|
83
86
|
|
|
84
87
|
// vendor -> flow record. At most one live login terminal per vendor.
|
|
85
88
|
var _flows = Object.create(null);
|
|
@@ -181,13 +184,17 @@ function attachVendorLogin(ctx) {
|
|
|
181
184
|
// the credentials the login just wrote. Shared adapter instances (Claude) are
|
|
182
185
|
// reused by every project, so tearing one down here would kill unrelated
|
|
183
186
|
// sessions; those runtimes pick up new credentials on their own.
|
|
184
|
-
function refreshVendorAuth(vendor) {
|
|
187
|
+
function refreshVendorAuth(vendor, linuxUser) {
|
|
185
188
|
var adapter = adapters[vendor];
|
|
186
189
|
if (!adapter || typeof adapter.shutdown !== "function") return Promise.resolve(false);
|
|
187
190
|
if (adapter.shared) {
|
|
188
191
|
console.log("[vendor-login] " + vendor + " adapter is shared; skipping restart for " + slug);
|
|
189
192
|
return Promise.resolve(false);
|
|
190
193
|
}
|
|
194
|
+
if (typeof adapter.refreshAuthIdentity === "function") {
|
|
195
|
+
return Promise.resolve(adapter.refreshAuthIdentity(linuxUser || null));
|
|
196
|
+
}
|
|
197
|
+
if (linuxUser) return Promise.resolve(false);
|
|
191
198
|
// adapter.shutdown() already fans out to every per-OS-user runtime it
|
|
192
199
|
// created (shutdownUserRuntimes), so one call covers isolated setups too.
|
|
193
200
|
return Promise.resolve()
|
|
@@ -209,7 +216,16 @@ function attachVendorLogin(ctx) {
|
|
|
209
216
|
flow.finishing = true;
|
|
210
217
|
flow.completed = !!succeeded;
|
|
211
218
|
|
|
212
|
-
|
|
219
|
+
// The next session must not reuse the pre-login "not authenticated"
|
|
220
|
+
// result. Re-check before restarting the runtime, which will then read
|
|
221
|
+
// the newly written credential file.
|
|
222
|
+
if (succeeded) yoke.invalidateAuthCache();
|
|
223
|
+
var refresh = Promise.resolve(false);
|
|
224
|
+
if (succeeded) {
|
|
225
|
+
refresh = typeof refreshVendorAuthEverywhere === "function"
|
|
226
|
+
? refreshVendorAuthEverywhere(vendor, flow.linuxUser || null)
|
|
227
|
+
: refreshVendorAuth(vendor, flow.linuxUser || null);
|
|
228
|
+
}
|
|
213
229
|
refresh.then(function (restarted) {
|
|
214
230
|
discardFlow(vendor);
|
|
215
231
|
closeFlowTerminal(flow);
|
|
@@ -391,6 +407,7 @@ function attachVendorLogin(ctx) {
|
|
|
391
407
|
listFlows: listFlows,
|
|
392
408
|
forgetTerminal: forgetTerminal,
|
|
393
409
|
isLoginTerminal: isLoginTerminal,
|
|
410
|
+
refreshVendorAuth: refreshVendorAuth,
|
|
394
411
|
};
|
|
395
412
|
}
|
|
396
413
|
|
package/lib/project.js
CHANGED
|
@@ -1610,6 +1610,7 @@ function createProjectContext(opts) {
|
|
|
1610
1610
|
getOsUserInfoForWs: getOsUserInfoForWs,
|
|
1611
1611
|
getOsUserInfoForLinuxUser: getOsUserInfoForLinuxUser,
|
|
1612
1612
|
getLinuxUserForSession: getLinuxUserForSession,
|
|
1613
|
+
refreshVendorAuthEverywhere: opts.refreshVendorAuthEverywhere,
|
|
1613
1614
|
});
|
|
1614
1615
|
|
|
1615
1616
|
// --- Filesystem handler (delegated to project-filesystem.js) ---
|
|
@@ -2141,6 +2142,7 @@ function createProjectContext(opts) {
|
|
|
2141
2142
|
listKnowledgeFiles: _knowledge.listKnowledgeFiles,
|
|
2142
2143
|
refreshCapsuleCatalog: _capsuleCatalog.refreshSessions,
|
|
2143
2144
|
getNotificationsModule: function () { return _notifications; },
|
|
2145
|
+
refreshVendorAuth: _vendorLogin.refreshVendorAuth,
|
|
2144
2146
|
getSchedules: _loop.getSchedules,
|
|
2145
2147
|
importSchedule: _loop.importSchedule,
|
|
2146
2148
|
removeSchedule: _loop.removeSchedule,
|
|
@@ -61,6 +61,20 @@ function attachMessageProcessor(ctx) {
|
|
|
61
61
|
if (session._lastAuthEmit && (now - session._lastAuthEmit) < 5000) return;
|
|
62
62
|
session._lastAuthEmit = now;
|
|
63
63
|
|
|
64
|
+
// Codex can leave a transient 401 in its app-server event stream after a
|
|
65
|
+
// successful device login. Confirm the CLI's current credential state
|
|
66
|
+
// before interrupting another session with a login modal.
|
|
67
|
+
var vendor = session.vendor || "claude";
|
|
68
|
+
yoke.invalidateAuthCache();
|
|
69
|
+
var authState = yoke.checkAuth();
|
|
70
|
+
if (authState[vendor]) {
|
|
71
|
+
sendAndRecord(session, {
|
|
72
|
+
type: "error",
|
|
73
|
+
text: "Authentication is available, but " + vendor + " reported an auth-like error. Try again.",
|
|
74
|
+
});
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
64
78
|
var authUser = session.ownerId ? usersModule.findUserById(session.ownerId) : null;
|
|
65
79
|
var authLinuxUser = authUser && authUser.linuxUser ? authUser.linuxUser : null;
|
|
66
80
|
var canAutoLogin = !usersModule.isMultiUser()
|
|
@@ -76,7 +90,7 @@ function attachMessageProcessor(ctx) {
|
|
|
76
90
|
sendToSession(session, {
|
|
77
91
|
type: "auth_required",
|
|
78
92
|
text: authTitle,
|
|
79
|
-
vendor:
|
|
93
|
+
vendor: vendor,
|
|
80
94
|
loginCommand: loginCommand,
|
|
81
95
|
linuxUser: authLinuxUser,
|
|
82
96
|
canAutoLogin: canAutoLogin,
|
|
@@ -88,7 +102,7 @@ function attachMessageProcessor(ctx) {
|
|
|
88
102
|
slug: slug,
|
|
89
103
|
sessionId: session.localId,
|
|
90
104
|
ownerId: session.ownerId || null,
|
|
91
|
-
vendor:
|
|
105
|
+
vendor: vendor,
|
|
92
106
|
loginCommand: loginCommand,
|
|
93
107
|
linuxUser: authLinuxUser,
|
|
94
108
|
canAutoLogin: canAutoLogin,
|
package/lib/server.js
CHANGED
|
@@ -1155,6 +1155,24 @@ function createServer(opts) {
|
|
|
1155
1155
|
}
|
|
1156
1156
|
|
|
1157
1157
|
// --- Project management ---
|
|
1158
|
+
function refreshVendorAuthEverywhere(vendor, linuxUser) {
|
|
1159
|
+
var refreshes = [];
|
|
1160
|
+
projects.forEach(function (projectCtx) {
|
|
1161
|
+
if (!projectCtx || typeof projectCtx.refreshVendorAuth !== "function") return;
|
|
1162
|
+
try {
|
|
1163
|
+
refreshes.push(Promise.resolve(projectCtx.refreshVendorAuth(vendor, linuxUser)).catch(function (err) {
|
|
1164
|
+
console.error("[server] Cross-project " + vendor + " auth refresh failed:", err && err.message ? err.message : err);
|
|
1165
|
+
return false;
|
|
1166
|
+
}));
|
|
1167
|
+
} catch (err) {
|
|
1168
|
+
console.error("[server] Cross-project " + vendor + " auth refresh threw:", err && err.message ? err.message : err);
|
|
1169
|
+
}
|
|
1170
|
+
});
|
|
1171
|
+
return Promise.all(refreshes).then(function (results) {
|
|
1172
|
+
return results.some(function (refreshed) { return !!refreshed; });
|
|
1173
|
+
});
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1158
1176
|
function addProject(cwd, slug, title, icon, projectOwnerId, worktreeMeta, extraOpts) {
|
|
1159
1177
|
if (projects.has(slug)) return false;
|
|
1160
1178
|
var extra = extraOpts || {};
|
|
@@ -1327,6 +1345,7 @@ function createServer(opts) {
|
|
|
1327
1345
|
onDmMessage: handleDmMessage,
|
|
1328
1346
|
broadcastAll: broadcastAll,
|
|
1329
1347
|
notificationsModule: _globalNotifications,
|
|
1348
|
+
refreshVendorAuthEverywhere: refreshVendorAuthEverywhere,
|
|
1330
1349
|
getProject: function (s) { return projects.get(s) || null; },
|
|
1331
1350
|
isUserOnline: isUserOnline,
|
|
1332
1351
|
});
|
|
@@ -1943,6 +1943,23 @@ function createCodexAdapter(opts) {
|
|
|
1943
1943
|
]).then(function() { return true; });
|
|
1944
1944
|
},
|
|
1945
1945
|
|
|
1946
|
+
// A device login writes credentials for one OS identity. Refresh only
|
|
1947
|
+
// that identity in OS-isolated deployments; single-user projects restart
|
|
1948
|
+
// their sole app-server.
|
|
1949
|
+
refreshAuthIdentity: function(linuxUser) {
|
|
1950
|
+
if (!_runtimeLinuxUser && linuxUser) {
|
|
1951
|
+
var userRuntime = _userRuntimes[linuxUser];
|
|
1952
|
+
if (!userRuntime) return Promise.resolve(false);
|
|
1953
|
+
return userRuntime.shutdown().then(function() {
|
|
1954
|
+
delete _userRuntimes[linuxUser];
|
|
1955
|
+
return true;
|
|
1956
|
+
});
|
|
1957
|
+
}
|
|
1958
|
+
if (_runtimeLinuxUser && linuxUser !== _runtimeLinuxUser) return Promise.resolve(false);
|
|
1959
|
+
if (_requiresLinuxUser) return Promise.resolve(false);
|
|
1960
|
+
return adapter.shutdown();
|
|
1961
|
+
},
|
|
1962
|
+
|
|
1946
1963
|
shutdownIfIdle: function(idleMs) {
|
|
1947
1964
|
if (!_runtimeLinuxUser && Object.keys(_userRuntimes).length > 0) {
|
|
1948
1965
|
return shutdownUserRuntimes("shutdownIfIdle", idleMs).then(function(results) {
|
package/package.json
CHANGED