@runuai/host 0.2.0 → 0.2.1

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.
@@ -278,6 +278,11 @@ export interface SetupTaskDeps {
278
278
  * a failure emits the auth-expired note but never aborts task-up. Returns true
279
279
  * iff a token was injected.
280
280
  */
281
+ /** Tasks whose gh setup is currently in flight. task-up and the first
282
+ * channel-ensure both kick this off and can overlap; a second concurrent
283
+ * `gh auth login` would race the container's gh config, so we dedupe. */
284
+ const setupInFlight = new Set<string>();
285
+
281
286
  export async function setupTaskGithub(
282
287
  taskId: string,
283
288
  userId: string,
@@ -288,6 +293,8 @@ export async function setupTaskGithub(
288
293
  const _inject =
289
294
  deps.inject ?? ((t: string, tok: string) => injectIntoContainer(t, tok));
290
295
  const _schedule = deps.schedule ?? scheduleRefresh;
296
+ if (setupInFlight.has(taskId)) return false;
297
+ setupInFlight.add(taskId);
291
298
  try {
292
299
  if (_hasToken(userId)) {
293
300
  const tok = await _request(userId);
@@ -311,6 +318,8 @@ export async function setupTaskGithub(
311
318
  console.warn(`[github] task ${taskId}: gh setup failed: ${reason}`);
312
319
  authExpiredHandler?.(taskId, userId, reason);
313
320
  return false;
321
+ } finally {
322
+ setupInFlight.delete(taskId);
314
323
  }
315
324
  }
316
325
 
@@ -181,6 +181,16 @@ class Orchestrator {
181
181
  // container), using the creator's per-user key. Best-effort.
182
182
  setupTaskGitIdentity(channel.taskId, task.ownerName, task.ownerEmail);
183
183
 
184
+ // GitHub auth for `gh` (ADR-027): mint + inject the access token and
185
+ // (re)start its refresh schedule. Done here — not only at task-up — so it
186
+ // survives a host restart: recovery recreates the channel and re-runs
187
+ // ensureSessions, whereas the in-memory refresh timer from the original
188
+ // task-up is gone. Guarded + best-effort + non-blocking; the token lands
189
+ // before the agents (spawned just below) make their first `gh` call.
190
+ if (task.ownerUserId) {
191
+ void setupTaskGithub(channel.taskId, task.ownerUserId);
192
+ }
193
+
184
194
  for (const agent of channel.roster) {
185
195
  const session = await this.factory.create({
186
196
  taskId: channel.taskId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runuai/host",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Uai host — runs ephemeral AI coding tasks in Docker on a machine you control.",
5
5
  "license": "MIT",
6
6
  "author": "Diogo Perillo <diogo.perillo@gmail.com>",