@awak-app/simy-cli 0.5.62 → 0.5.63
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/package.json +1 -1
- package/src/agent.js +19 -7
- package/src/cli-contract.js +1 -1
- package/src/desktop-access.js +1 -1
- package/src/desktop-host.js +12 -5
package/package.json
CHANGED
package/src/agent.js
CHANGED
|
@@ -199,6 +199,7 @@ export async function startAgent({
|
|
|
199
199
|
let organizationState = "unknown";
|
|
200
200
|
let organizationRefresh = null;
|
|
201
201
|
let pendingOrganization = null;
|
|
202
|
+
let desktopLoginPending = false;
|
|
202
203
|
await cleanupExpiredRuns();
|
|
203
204
|
for (const task of await restoreLocalTasks({ root: taskStorageRoot })) {
|
|
204
205
|
localTaskRegistry.create(task);
|
|
@@ -469,7 +470,9 @@ export async function startAgent({
|
|
|
469
470
|
})();
|
|
470
471
|
return organizationRefresh;
|
|
471
472
|
};
|
|
472
|
-
const requestDesktopLogin = (organizationId = null) => {
|
|
473
|
+
const requestDesktopLogin = (organizationId = null, { passive = false } = {}) => {
|
|
474
|
+
// Reopening the verification page must keep the original nonce and scope.
|
|
475
|
+
if (!organizationId && pendingOrganization) return loginUrl?.toString() ?? null;
|
|
473
476
|
let organizationSlug = null;
|
|
474
477
|
if (organizationId) {
|
|
475
478
|
if (!isSessionValid(session, Date.now(), apiOrigin)) throw new Error("Sign in before switching organizations.");
|
|
@@ -486,8 +489,10 @@ export async function startAgent({
|
|
|
486
489
|
notifyDesktopStatusChange();
|
|
487
490
|
} else if (isSessionValid(session, Date.now(), apiOrigin)) {
|
|
488
491
|
if (!enforceDesktopAccess || desktopAccess.snapshot().state !== 'signed_out') return null;
|
|
489
|
-
|
|
490
|
-
|
|
492
|
+
if (!passive || !loginUrl) {
|
|
493
|
+
authNonce = randomBytes(16).toString("base64url");
|
|
494
|
+
loginUrl = null;
|
|
495
|
+
}
|
|
491
496
|
}
|
|
492
497
|
if (!loginUrl) {
|
|
493
498
|
loginUrl = new URL("/local-cli/connect", apiOrigin);
|
|
@@ -501,6 +506,7 @@ export async function startAgent({
|
|
|
501
506
|
loginUrl.searchParams.set('org_slug', organizationSlug);
|
|
502
507
|
}
|
|
503
508
|
}
|
|
509
|
+
if (!passive) { desktopLoginPending = true; notifyDesktopStatusChange(); }
|
|
504
510
|
return loginUrl.toString();
|
|
505
511
|
};
|
|
506
512
|
|
|
@@ -691,6 +697,8 @@ export async function startAgent({
|
|
|
691
697
|
if (session?.device_id && session.token) reclaimableSession = session;
|
|
692
698
|
session = null;
|
|
693
699
|
loginUrl = null;
|
|
700
|
+
pendingOrganization = null;
|
|
701
|
+
desktopLoginPending = false;
|
|
694
702
|
refreshAvailableCapabilities();
|
|
695
703
|
requestDesktopLogin();
|
|
696
704
|
}
|
|
@@ -1194,6 +1202,7 @@ export async function startAgent({
|
|
|
1194
1202
|
await writeSession(apiOrigin, session, sessionRoot);
|
|
1195
1203
|
authNonce = randomBytes(16).toString("base64url");
|
|
1196
1204
|
pendingOrganization = null;
|
|
1205
|
+
desktopLoginPending = false;
|
|
1197
1206
|
sqmEnabled = await readSqmEnabled(sessionRoot, apiOrigin, session);
|
|
1198
1207
|
if (changingScope) sqmLocalReviewWorker?.resetHistory?.();
|
|
1199
1208
|
organizationState = "unknown";
|
|
@@ -2156,7 +2165,7 @@ export async function startAgent({
|
|
|
2156
2165
|
}
|
|
2157
2166
|
|
|
2158
2167
|
if (!isSessionValid(session, Date.now(), apiOrigin)) {
|
|
2159
|
-
requestDesktopLogin();
|
|
2168
|
+
requestDesktopLogin(null, { passive: true });
|
|
2160
2169
|
if (!quiet) {
|
|
2161
2170
|
console.log(`Sign in to SIMY: ${loginUrl.toString()}`);
|
|
2162
2171
|
console.log(`Session file: ${sessionPath(apiOrigin, sessionRoot)}`);
|
|
@@ -2242,6 +2251,7 @@ export async function startAgent({
|
|
|
2242
2251
|
version: CLI_VERSION,
|
|
2243
2252
|
webOrigin: apiOrigin,
|
|
2244
2253
|
auth,
|
|
2254
|
+
authorization: { pending: Boolean(pendingOrganization || desktopLoginPending) },
|
|
2245
2255
|
...(enforceDesktopAccess ? { access: { ...desktopAccess.snapshot(), revision: desktopScopeRevision() } } : {}),
|
|
2246
2256
|
connection: connectionStatus.snapshot({ authenticated: auth.state === "signed_in" }),
|
|
2247
2257
|
tasks: desktopTaskStatus([registry, directRegistry, localTaskRegistry]),
|
|
@@ -2256,7 +2266,8 @@ export async function startAgent({
|
|
|
2256
2266
|
codex,
|
|
2257
2267
|
claude,
|
|
2258
2268
|
providers: { codex, claude },
|
|
2259
|
-
|
|
2269
|
+
// URLs stay inside the host; native UI reopens the current request by action.
|
|
2270
|
+
loginUrl: auth.state === "signed_in" ? null : requestDesktopLogin(null, { passive: true }),
|
|
2260
2271
|
};
|
|
2261
2272
|
};
|
|
2262
2273
|
return {
|
|
@@ -2270,9 +2281,10 @@ export async function startAgent({
|
|
|
2270
2281
|
shutdown,
|
|
2271
2282
|
webOrigin: apiOrigin,
|
|
2272
2283
|
get loginUrl() {
|
|
2273
|
-
return requestDesktopLogin();
|
|
2284
|
+
return requestDesktopLogin(null, { passive: true });
|
|
2274
2285
|
},
|
|
2275
2286
|
requestDesktopLogin,
|
|
2287
|
+
getPendingAuthorizationUrl: () => pendingOrganization || desktopLoginPending ? loginUrl?.toString() ?? null : null,
|
|
2276
2288
|
async refreshDesktopAccess() {
|
|
2277
2289
|
const previous = desktopAccess.snapshot().state;
|
|
2278
2290
|
const result = await desktopAccess.refresh(true);
|
|
@@ -2284,7 +2296,7 @@ export async function startAgent({
|
|
|
2284
2296
|
getDesktopAccess: () => ({ ...desktopAccess.snapshot(), revision: desktopScopeRevision() }),
|
|
2285
2297
|
requireDesktopAccess: authorizeDesktopWork,
|
|
2286
2298
|
refreshOrganizations,
|
|
2287
|
-
cancelOrganizationSelection() { authNonce = randomBytes(16).toString("base64url"); pendingOrganization = null; loginUrl = null; refreshAvailableCapabilities(); notifyDesktopStatusChange(); },
|
|
2299
|
+
cancelOrganizationSelection() { authNonce = randomBytes(16).toString("base64url"); pendingOrganization = null; desktopLoginPending = false; loginUrl = null; refreshAvailableCapabilities(); notifyDesktopStatusChange(); },
|
|
2288
2300
|
async setSqmEnabled(enabled) {
|
|
2289
2301
|
if (enabled) await authorizeDesktopWork();
|
|
2290
2302
|
if (!isSessionValid(session, Date.now(), apiOrigin) || pendingOrganization) throw new Error("Sign in to an organization first.");
|
package/src/cli-contract.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
} from "./durable-local-task-steps-contract.js";
|
|
14
14
|
import { buildExecutionCapabilityContract } from "./execution-capability-contract.js";
|
|
15
15
|
|
|
16
|
-
export const CLI_VERSION = "0.5.
|
|
16
|
+
export const CLI_VERSION = "0.5.63";
|
|
17
17
|
export const CLI_API_CONTRACT_VERSION = 10;
|
|
18
18
|
|
|
19
19
|
export function withCliContract(
|
package/src/desktop-access.js
CHANGED
|
@@ -7,7 +7,7 @@ export function validDesktopAccessPath(path, slug) {
|
|
|
7
7
|
return path === expected || path === `${expected}/`;
|
|
8
8
|
}
|
|
9
9
|
export const desktopAccessReady = value => value?.state === 'ready';
|
|
10
|
-
const recoveryActions = new Set(['refresh', 'reconnect', 'login', 'organization_select', 'organization_cancel', 'organizations_refresh',
|
|
10
|
+
const recoveryActions = new Set(['refresh', 'reconnect', 'login', 'authorization_open', 'organization_select', 'organization_cancel', 'organizations_refresh',
|
|
11
11
|
'check_update', 'update', 'quit', 'delivery_stop', 'delivery_pause', 'delivery_reject',
|
|
12
12
|
'desktop_access_refresh', 'desktop_access_settings', 'desktop_access_members']);
|
|
13
13
|
export function desktopActionAllowed(access, command) {
|
package/src/desktop-host.js
CHANGED
|
@@ -39,6 +39,7 @@ const silentLogger = { log() {}, error() {} };
|
|
|
39
39
|
const MAX_DESKTOP_FRAME_BYTES = 8 * 1024 * 1024;
|
|
40
40
|
|
|
41
41
|
function desktopActionError(action, error) {
|
|
42
|
+
if (action === 'authorization_open' || error?.code === 'desktop_browser_unavailable') return "Could not open your browser. Open the verification link again, or check your default browser.";
|
|
42
43
|
if (error?.code === "desktop_access_required") return "Sign in to an organization with SQM and Developer access before using SIMY.";
|
|
43
44
|
if (action === "session_deadline_decide") return error?.code === "conflict"
|
|
44
45
|
? "Deadline changed; refresh before deciding." : "The deadline is unavailable. Refresh this conversation before deciding.";
|
|
@@ -436,16 +437,22 @@ export async function startDesktopHost({ input = process.stdin, output = process
|
|
|
436
437
|
else if (action === "organizations_refresh") await agent.refreshOrganizations();
|
|
437
438
|
else if (action === "sqm_set_enabled") await agent.setSqmEnabled(command.enabled);
|
|
438
439
|
else if (action === "organization_cancel") agent.cancelOrganizationSelection();
|
|
439
|
-
else if (action === "login" || action === "organization_select") {
|
|
440
|
+
else if (action === "login" || action === "organization_select" || action === "authorization_open") {
|
|
440
441
|
if (action === "organization_select" && (typeof command.organizationId !== "string" || !command.organizationId)) throw new Error("Select an organization first.");
|
|
441
442
|
if (action === "organization_select" && activeQueuedAction && !["refresh", "reconnect", "task_detail", "organizations_refresh", "check_update"].includes(activeQueuedAction)) {
|
|
442
443
|
throw Object.assign(new Error("Wait for the current action to finish before switching organizations."), { code: "desktop_organization_busy" });
|
|
443
444
|
}
|
|
444
|
-
const url =
|
|
445
|
+
const url = action === "authorization_open" ? agent.getPendingAuthorizationUrl?.()
|
|
446
|
+
: await agent.requestDesktopLogin(action === "organization_select" ? command.organizationId : null);
|
|
447
|
+
if (action === 'authorization_open' && !url) throw new Error('No pending authorization.');
|
|
445
448
|
if (url) {
|
|
446
449
|
if (new URL(url).origin !== webOrigin) throw new Error("Invalid login origin.");
|
|
447
|
-
|
|
448
|
-
|
|
450
|
+
// Keep the pending request after an OS/browser failure so the user can
|
|
451
|
+
// use the visible fallback without silently canceling authorization.
|
|
452
|
+
try {
|
|
453
|
+
const result = await (dependencies.openLogin || openAuthorizationUrl)(url, { logger: silentLogger });
|
|
454
|
+
if (!result.opened) throw new Error('Browser unavailable.');
|
|
455
|
+
} catch { throw Object.assign(new Error('Browser unavailable.'), { code: 'desktop_browser_unavailable' }); }
|
|
449
456
|
}
|
|
450
457
|
} else if (action === "check_update") await updater.checkNow();
|
|
451
458
|
else if (action === "update") {
|
|
@@ -560,7 +567,7 @@ export async function startDesktopHost({ input = process.stdin, output = process
|
|
|
560
567
|
emit({ type: "result", id: command.id, ok: false, message: desktopActionError(command.action, error) });
|
|
561
568
|
}
|
|
562
569
|
};
|
|
563
|
-
if (["login", "organization_select", "organization_cancel"].includes(command.action)) {
|
|
570
|
+
if (["login", "organization_select", "organization_cancel", "authorization_open"].includes(command.action)) {
|
|
564
571
|
authorizationQueue = authorizationQueue.then(execute).catch(() => {});
|
|
565
572
|
} else {
|
|
566
573
|
queue = queue.then(async () => {
|