feat(flows): sealed-aware flow list — hub sample flag, own dir, sample import

Three connection-truth fixes on the flow surface:

* The editor now lists the CONNECTED hub's flows: inside a sealed
  area Studio passes the instance's own flows dir
  (~/.chain/sealed/<slug>/data/flows) — previously the editor kept
  showing the shared hub's files whatever the connection, so a
  sealed area's list was simply wrong (and runSavedFlow hit the
  other hub's namespace).
* A connection switch replaces the editor state entirely (keyed by
  the sealed slug): an open buffer from one context never survives
  into the other — same privacy class as the switch race.
* Sample truth comes from the hub: listFlows' FlowSummary.sample
  (regenerated Dart SDK stubs) feeds the editor's sampleFlowNames;
  unknown (old hub / fetch failed) means no chips. Inside a sealed
  area the empty list offers the deliberate 'import example flows'
  action via chain flows import-samples against the instance's own
  dirs; the shared hub gets no such offer (it seeds samples itself,
  and a deliberate deletion is respected).

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
flemming-it 2026-08-28 00:11:55 +02:00
parent 64c2a77dc9
commit d69277d29d
4 changed files with 107 additions and 1 deletions

View file

@ -1002,6 +1002,7 @@ class HubService {
sizeBytes: f.sizeBytes.toInt(),
requiredCapabilities: List<String>.from(f.requiredCapabilities),
project: f.project,
sample: f.sample,
),
)
.toList()
@ -1593,12 +1594,19 @@ class SavedFlow {
/// project, then `general`).
final String project;
/// True when the file still carries the bundled-sample header
/// it arrived via the hub's sample import, not from an operator.
/// Old hubs without the wire field report false, so no chip is
/// ever shown on guesswork.
final bool sample;
const SavedFlow({
required this.name,
required this.path,
required this.sizeBytes,
required this.requiredCapabilities,
this.project = '',
this.sample = false,
});
}

View file

@ -239,6 +239,31 @@ class SystemActions {
return _runFai(['project', 'start', slug]);
}
/// Import the bundled sample flows (`chain flows import-samples`;
/// existing files are kept). With [sealedSlug] the import targets
/// that sealed instance's own data/modules dirs — sealed areas
/// start without samples, this is the deliberate pull.
static Future<({bool ok, String stdout, String stderr})>
chainFlowsImportSamples({String? sealedSlug}) async {
if (sealedSlug == null || sealedSlug.isEmpty) {
return _runFai(['flows', 'import-samples']);
}
final home =
Platform.environment['HOME'] ??
Platform.environment['USERPROFILE'] ??
'';
final sep = Platform.pathSeparator;
final root = '$home$sep.chain${sep}sealed$sep$sealedSlug';
return _runFai([
'flows',
'import-samples',
'--data-dir',
'$root${sep}data',
'--modules-dir',
'$root${sep}modules',
]);
}
static Future<({bool ok, String stdout, String stderr})> _runFai(
List<String> args,
) async {