snow-flow 10.0.1-dev.415 → 10.0.1-dev.417

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
- "version": "10.0.1-dev.415",
3
+ "version": "10.0.1-dev.417",
4
4
  "name": "snow-flow",
5
5
  "description": "Snow-Flow - ServiceNow Multi-Agent Development Framework powered by AI",
6
6
  "license": "Elastic-2.0",
@@ -1261,6 +1261,7 @@ function DialogAuthEnterpriseCombined() {
1261
1261
  | "code"
1262
1262
  | "verifying-enterprise"
1263
1263
  | "checking-portal-sn"
1264
+ | "select-sn-instance"
1264
1265
  | "sn-method"
1265
1266
  | "sn-instance"
1266
1267
  | "sn-oauth-clientid"
@@ -1285,6 +1286,18 @@ function DialogAuthEnterpriseCombined() {
1285
1286
  clientSecret?: string
1286
1287
  } | null>(null)
1287
1288
 
1289
+ // ServiceNow instances from enterprise portal (for multi-instance selection)
1290
+ const [snInstances, setSnInstances] = createSignal<
1291
+ Array<{
1292
+ id: number
1293
+ instanceName: string
1294
+ instanceUrl: string
1295
+ environmentType: string
1296
+ isDefault: boolean
1297
+ enabled: boolean
1298
+ }>
1299
+ >([])
1300
+
1288
1301
  // ServiceNow state
1289
1302
  const [snMethod, setSnMethod] = createSignal<"oauth" | "basic">("oauth")
1290
1303
  const [snInstance, setSnInstance] = createSignal("")
@@ -1345,6 +1358,9 @@ function DialogAuthEnterpriseCombined() {
1345
1358
  setSessionId("")
1346
1359
  setAuthCode("")
1347
1360
  setTimeout(() => subdomainInput?.focus(), 10)
1361
+ } else if (currentStep === "select-sn-instance") {
1362
+ // Can't re-do enterprise auth, go back to main auth menu
1363
+ dialog.replace(() => <DialogAuth />)
1348
1364
  } else if (currentStep === "sn-method") {
1349
1365
  // Can't go back to enterprise flow, go to main menu
1350
1366
  dialog.replace(() => <DialogAuth />)
@@ -1533,28 +1549,42 @@ function DialogAuthEnterpriseCombined() {
1533
1549
  duration: 3000,
1534
1550
  })
1535
1551
 
1536
- // Check if enterprise portal has ServiceNow credentials
1552
+ // Check if enterprise portal has ServiceNow instances
1537
1553
  setStep("checking-portal-sn")
1538
- const portalCreds = await fetchPortalSnCredentials(portalUrl, data.token)
1554
+ const instances = await fetchPortalSnInstances(portalUrl, data.token)
1539
1555
 
1540
- if (portalCreds) {
1541
- // Portal has ServiceNow credentials - use them directly
1542
- setPortalSnCredentials(portalCreds)
1556
+ if (instances.length === 0) {
1557
+ // No instances manual setup
1558
+ toast.show({
1559
+ variant: "info",
1560
+ message: "No ServiceNow instances found on portal. Please configure manually.",
1561
+ duration: 3000,
1562
+ })
1563
+ setStep("sn-method")
1564
+ } else if (instances.length === 1) {
1565
+ // Single instance — fetch creds and proceed
1543
1566
  toast.show({
1544
1567
  variant: "info",
1545
- message: "ServiceNow credentials found in enterprise portal!",
1568
+ message: `ServiceNow instance found: ${instances[0].instanceName}`,
1546
1569
  duration: 3000,
1547
1570
  })
1548
- // Skip manual ServiceNow input, go directly to completing
1549
- await startBothMcpServersWithPortalCreds(portalUrl, data.token, portalCreds, data.user)
1571
+ const creds = await fetchPortalSnInstanceById(portalUrl, data.token, instances[0].id)
1572
+ if (creds) {
1573
+ setPortalSnCredentials(creds)
1574
+ await startBothMcpServersWithPortalCreds(portalUrl, data.token, creds, data.user)
1575
+ } else {
1576
+ toast.show({ variant: "error", message: "Failed to fetch instance credentials.", duration: 3000 })
1577
+ setStep("sn-method")
1578
+ }
1550
1579
  } else {
1551
- // No ServiceNow credentials on portal - ask user to input manually
1580
+ // Multiple instances show selection
1581
+ setSnInstances(instances)
1552
1582
  toast.show({
1553
1583
  variant: "info",
1554
- message: "No ServiceNow credentials found on portal. Please configure manually.",
1584
+ message: `${instances.length} ServiceNow instances found. Please select one.`,
1555
1585
  duration: 3000,
1556
1586
  })
1557
- setStep("sn-method")
1587
+ setStep("select-sn-instance")
1558
1588
  }
1559
1589
  } catch (e) {
1560
1590
  toast.show({ variant: "error", message: e instanceof Error ? e.message : "Verification failed" })
@@ -1565,38 +1595,58 @@ function DialogAuthEnterpriseCombined() {
1565
1595
 
1566
1596
  // === Portal ServiceNow Credentials ===
1567
1597
 
1568
- const fetchPortalSnCredentials = async (
1598
+ const fetchPortalSnInstances = async (
1569
1599
  portalUrl: string,
1570
- token: string
1571
- ): Promise<{ instanceUrl: string; clientId: string; clientSecret: string } | null> => {
1600
+ token: string,
1601
+ ): Promise<
1602
+ Array<{
1603
+ id: number
1604
+ instanceName: string
1605
+ instanceUrl: string
1606
+ environmentType: string
1607
+ isDefault: boolean
1608
+ enabled: boolean
1609
+ }>
1610
+ > => {
1572
1611
  try {
1573
- const response = await fetch(`${portalUrl}/api/user-credentials/servicenow/default`, {
1612
+ const response = await fetch(`${portalUrl}/api/user-credentials/servicenow/instances`, {
1574
1613
  method: "GET",
1575
1614
  headers: {
1576
1615
  Authorization: `Bearer ${token}`,
1577
1616
  Accept: "application/json",
1578
1617
  },
1579
1618
  })
1580
-
1581
- if (!response.ok) {
1582
- return null
1583
- }
1584
-
1619
+ if (!response.ok) return []
1585
1620
  const data = await response.json()
1621
+ if (!data.success || !Array.isArray(data.instances)) return []
1622
+ return data.instances.filter((i: any) => i.enabled)
1623
+ } catch {
1624
+ return []
1625
+ }
1626
+ }
1586
1627
 
1587
- if (!data.success || !data.instance) {
1588
- return null
1589
- }
1590
-
1591
- const instance = data.instance
1592
- if (!instance.instanceUrl || !instance.clientId || !instance.clientSecret) {
1593
- return null
1594
- }
1595
-
1628
+ const fetchPortalSnInstanceById = async (
1629
+ portalUrl: string,
1630
+ token: string,
1631
+ instanceId: number,
1632
+ ): Promise<{ instanceUrl: string; clientId: string; clientSecret: string } | null> => {
1633
+ try {
1634
+ const response = await fetch(`${portalUrl}/api/user-credentials/servicenow/instances/${instanceId}`, {
1635
+ method: "GET",
1636
+ headers: {
1637
+ Authorization: `Bearer ${token}`,
1638
+ Accept: "application/json",
1639
+ },
1640
+ })
1641
+ if (!response.ok) return null
1642
+ const data = await response.json()
1643
+ if (!data.success || !data.instance) return null
1644
+ const inst = data.instance
1645
+ if (!inst.instanceUrl || !inst.clientId || !inst.clientSecret) return null
1596
1646
  return {
1597
- instanceUrl: instance.instanceUrl,
1598
- clientId: instance.clientId,
1599
- clientSecret: instance.clientSecret,
1647
+ instanceUrl: inst.instanceUrl,
1648
+ clientId: inst.clientId,
1649
+ clientSecret: inst.clientSecret,
1600
1650
  }
1601
1651
  } catch {
1602
1652
  return null
@@ -1952,6 +2002,33 @@ function DialogAuthEnterpriseCombined() {
1952
2002
  </box>
1953
2003
  </Show>
1954
2004
 
2005
+ {/* Step 4c: Select ServiceNow instance */}
2006
+ <Show when={step() === "select-sn-instance"}>
2007
+ <DialogSelect
2008
+ title="Select ServiceNow Instance"
2009
+ options={snInstances().map((inst) => ({
2010
+ title: inst.instanceName,
2011
+ value: String(inst.id),
2012
+ description: inst.instanceUrl,
2013
+ footer: inst.environmentType + (inst.isDefault ? " (default)" : ""),
2014
+ category: "ServiceNow Instances",
2015
+ onSelect: async () => {
2016
+ setStep("completing")
2017
+ const portalUrl = `https://${subdomain().trim().toLowerCase()}.snow-flow.dev`
2018
+ const token = enterpriseData().token!
2019
+ const creds = await fetchPortalSnInstanceById(portalUrl, token, inst.id)
2020
+ if (creds) {
2021
+ setPortalSnCredentials(creds)
2022
+ await startBothMcpServersWithPortalCreds(portalUrl, token, creds, enterpriseData().user)
2023
+ } else {
2024
+ toast.show({ variant: "error", message: "Failed to fetch instance credentials.", duration: 3000 })
2025
+ setStep("sn-method")
2026
+ }
2027
+ },
2028
+ }))}
2029
+ />
2030
+ </Show>
2031
+
1955
2032
  {/* Step 5: Choose ServiceNow method */}
1956
2033
  <Show when={step() === "sn-method"}>
1957
2034
  <box gap={1}>
@@ -335,23 +335,39 @@ async function createFlowViaScheduledJob(
335
335
  " }",
336
336
  " } catch(snapRefE) { r.steps.reference_flow.snapshot_error = snapRefE + ''; }",
337
337
  " }",
338
- // Count trigger/action instances for reference flow
338
+ // Count trigger/action instances for reference flow AND its snapshot
339
339
  " var refTrig = new GlideAggregate('sys_hub_trigger_instance');",
340
340
  " refTrig.addQuery('flow', refId); refTrig.addAggregate('COUNT'); refTrig.query();",
341
- " r.steps.reference_flow.trigger_count = refTrig.next() ? parseInt(refTrig.getAggregate('COUNT')) : 0;",
341
+ " r.steps.reference_flow.trigger_count_on_flow = refTrig.next() ? parseInt(refTrig.getAggregate('COUNT')) : 0;",
342
342
  " var refAct = new GlideAggregate('sys_hub_action_instance');",
343
343
  " refAct.addQuery('flow', refId); refAct.addAggregate('COUNT'); refAct.query();",
344
- " r.steps.reference_flow.action_count = refAct.next() ? parseInt(refAct.getAggregate('COUNT')) : 0;",
344
+ " r.steps.reference_flow.action_count_on_flow = refAct.next() ? parseInt(refAct.getAggregate('COUNT')) : 0;",
345
+ // KEY: check if snapshot has its OWN action/trigger instances
346
+ " if (refLs !== 'null' && refLs.length === 32) {",
347
+ " var snapTrig = new GlideAggregate('sys_hub_trigger_instance');",
348
+ " snapTrig.addQuery('flow', refLs); snapTrig.addAggregate('COUNT'); snapTrig.query();",
349
+ " r.steps.reference_flow.trigger_count_on_snapshot = snapTrig.next() ? parseInt(snapTrig.getAggregate('COUNT')) : 0;",
350
+ " var snapAct = new GlideAggregate('sys_hub_action_instance');",
351
+ " snapAct.addQuery('flow', refLs); snapAct.addAggregate('COUNT'); snapAct.query();",
352
+ " r.steps.reference_flow.action_count_on_snapshot = snapAct.next() ? parseInt(snapAct.getAggregate('COUNT')) : 0;",
353
+ // Check sys_class_name of the snapshot (to understand table hierarchy)
354
+ " try {",
355
+ " var snapMeta = new GlideRecord('sys_metadata');",
356
+ " if (snapMeta.get(refLs)) {",
357
+ " r.steps.reference_flow.snapshot_sys_class = snapMeta.getValue('sys_class_name') + '';",
358
+ " }",
359
+ " } catch(e) {}",
360
+ // Check table hierarchy: does sys_hub_flow_snapshot extend sys_hub_flow?
361
+ " try {",
362
+ " var td = GlideDBObjectManager.get().getAbsoluteBase('sys_hub_flow_snapshot');",
363
+ " r.steps.reference_flow.snapshot_base_table = td + '';",
364
+ " } catch(e) { r.steps.reference_flow.snapshot_base_table = 'error'; }",
365
+ " }",
345
366
  " }",
346
367
  " } catch(refE) { r.steps.reference_flow = { error: refE + '' }; }",
347
368
  "",
348
- // ── Engine: skip compile (returns error, doesn't help) ──
349
- " r.steps.engine = { mode: 'probe_only' };",
350
- " try {",
351
- " if (typeof sn_fd !== 'undefined') {",
352
- " r.steps.engine.sn_fd = 'available';",
353
- " } else { r.steps.engine.sn_fd = 'unavailable'; }",
354
- " } catch(engineErr) {}",
369
+ // ── No engine calls probe only ──
370
+ " r.steps.engine = { mode: 'probe_only', sn_fd: typeof sn_fd !== 'undefined' ? 'available' : 'unavailable' };",
355
371
  " }",
356
372
  "",
357
373
  " r.flow_sys_id = flowSysId ? flowSysId + '' : null;",