Files
wehub-resource-sync 8cb1f9f479
Publish SDK (PyPI) / publish (push) Waiting to run
Publish SDK (npm) / publish (@aionui/officecli-sdk) (push) Waiting to run
Publish SDK (npm) / publish (@officecli/officecli-sdk) (push) Waiting to run
Publish SDK (npm) / publish (@officecli/sdk) (push) Waiting to run
Publish SDK (npm) / publish (officecli-sdk) (push) Waiting to run
SDK smoke / smoke (windows-latest) (push) Waiting to run
SDK smoke / smoke (macos-latest) (push) Waiting to run
SDK smoke / smoke (ubuntu-latest) (push) Waiting to run
Skill parity / diff (push) Waiting to run
chore: import upstream snapshot with attribution
2026-07-13 13:09:29 +08:00

79 lines
3.7 KiB
C#

// Copyright 2026 OfficeCLI (https://OfficeCLI.AI)
// SPDX-License-Identifier: Apache-2.0
using System.CommandLine;
using OfficeCli.Core;
namespace OfficeCli;
static partial class CommandBuilder
{
// ==================== save command ====================
//
// Flush the resident's in-memory document to disk WITHOUT ending the
// session. Only meaningful inside a running resident — agents that build
// a workbook incrementally and need mid-build snapshots (e.g. for a
// third-party Excel viewer that ingests the .xlsx package directly) use
// this to recover the parse-amortization benefit of resident mode while
// still publishing snapshots on demand.
//
// Non-resident mode is rejected on purpose: each non-resident command
// already opens-mutates-closes (close = save), so there's no pending
// in-memory state to flush. A no-op success would invite confused
// "save just in case" code; an error tells the user they're in the
// wrong mode.
private static Command BuildSaveCommand(Option<bool> jsonOption)
{
var saveFileArg = new Argument<FileInfo>("file") { Description = "Office document path" };
var saveCommand = new Command("save", "Flush in-memory changes to disk, keeping the resident running. Run before a non-officecli program reads the file (officecli's own reads always see edits; a direct disk reader sees the pre-edit file until a flush). A live resident also auto-flushes shortly after going idle (adaptive 2-10s; see OFFICECLI_RESIDENT_FLUSH: each|auto|<seconds>|off). No-op if no resident is active.");
saveCommand.Add(saveFileArg);
saveCommand.Add(jsonOption);
saveCommand.SetAction(result => { var json = result.GetValue(jsonOption); return SafeRun(() =>
{
var file = result.GetValue(saveFileArg)!;
var filePath = file.FullName;
// Probe without auto-starting. `save` is meaningless without a
// pre-existing in-memory session, so we deliberately skip the
// TryResident auto-start path that other verbs use.
if (!ResidentClient.TryConnect(filePath, out _))
{
// No resident session to flush. In the non-resident model the
// document on disk is already current (each mutation eager-saved),
// so save is a no-op SUCCESS rather than an error — keeping
// "edit, then save/close" a safe habit regardless of backend.
var msg = $"{file.Name} is already saved to disk.";
if (json)
Console.WriteLine(OutputFormatter.WrapEnvelopeText(msg));
else
Console.WriteLine(msg);
return 0;
}
var request = new ResidentRequest { Command = "save", Json = json };
var response = ResidentClient.TrySend(
filePath, request,
maxRetries: ResidentBusyMaxRetries,
connectTimeoutMs: ResidentBusyConnectTimeoutMs);
if (response == null)
{
var msg = $"Resident for {file.Name} is running but the save command could not be delivered (main pipe busy or unresponsive).";
if (json)
Console.WriteLine(OutputFormatter.WrapEnvelopeError(msg));
else
Console.Error.WriteLine($"Error: {msg}");
return 3;
}
if (!string.IsNullOrEmpty(response.Stdout))
Console.WriteLine(response.Stdout);
if (!string.IsNullOrEmpty(response.Stderr))
Console.Error.WriteLine(response.Stderr);
return response.ExitCode;
}, json); });
return saveCommand;
}
}