@aion0/forge 0.10.48 → 0.10.49

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/RELEASE_NOTES.md CHANGED
@@ -1,12 +1,11 @@
1
- # Forge v0.10.48
1
+ # Forge v0.10.49
2
2
 
3
- Released: 2026-06-08
3
+ Released: 2026-06-09
4
4
 
5
- ## Changes since v0.10.47
5
+ ## Changes since v0.10.48
6
6
 
7
7
  ### Other
8
- - fix(proxy): drop runtime:'nodejs' Next.js 16 errors on it
9
- - Implement an MCP layer for forge management (#34)
8
+ - fix(idp-login): pick a failing SP as the SAML trigger, not the first one
10
9
 
11
10
 
12
- **Full Changelog**: https://github.com/aiwatching/forge/compare/v0.10.47...v0.10.48
11
+ **Full Changelog**: https://github.com/aiwatching/forge/compare/v0.10.48...v0.10.49
@@ -103,14 +103,36 @@ function readIdpBlocks(): IdpTemplateBlock[] {
103
103
  return tryRead() || [];
104
104
  }
105
105
 
106
- /** First installed SP from saml_sps wins as trigger. */
106
+ /** Pick the SP whose tab we'll open to trigger the SAML flow.
107
+ *
108
+ * Preference order:
109
+ * 1. A SP whose login-status cache currently shows ✗ — opening it forces
110
+ * a SAML redirect to the IdP, which is exactly what we want when the
111
+ * IdP cookie has expired even though some sibling SPs still have a
112
+ * valid session cookie (Mantis can be ✓ while pmdb/tp are ✗ because
113
+ * per-SP cookies are independent of the IdP SSO state).
114
+ * 2. Otherwise first installed SP with a base_url (legacy fallback).
115
+ *
116
+ * Without (1) the IdP login flow would open Mantis (first in saml_sps),
117
+ * see the cookie still good, declare "no form needed", and exit — leaving
118
+ * the broken siblings unrefreshed.
119
+ */
107
120
  function pickTriggerUrl(saml_sps: string[]): string | null {
121
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
122
+ const { getCachedResult } = require('./login-status') as typeof import('./login-status');
123
+ let fallback: string | null = null;
108
124
  for (const id of saml_sps) {
109
125
  const c = getInstalledConnector(id);
110
126
  const url = (c?.config as { base_url?: string } | undefined)?.base_url;
111
- if (url) return url.replace(/\/+$/, '/');
127
+ if (!url) continue;
128
+ const normalized = url.replace(/\/+$/, '/');
129
+ fallback ||= normalized;
130
+ const cached = getCachedResult(`connector:${id}`);
131
+ if (cached && cached.ok === false) {
132
+ return normalized;
133
+ }
112
134
  }
113
- return null;
135
+ return fallback;
114
136
  }
115
137
 
116
138
  async function runOneIdp(block: IdpTemplateBlock, req: IdpLoginRequest): Promise<IdpLoginEntry> {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aion0/forge",
3
- "version": "0.10.48",
3
+ "version": "0.10.49",
4
4
  "description": "Unified AI workflow platform — multi-model task orchestration, persistent sessions, web terminal, remote access",
5
5
  "type": "module",
6
6
  "scripts": {