Implement the bDS2 sync scripting API.
This commit is contained in:
@@ -10,7 +10,7 @@ The project is under active development. Core blogging workflows are broadly ava
|
||||
- Post and translation authoring with draft/published lifecycle, metadata, tags, categories, links, media, and batch gallery-image import.
|
||||
- Media import, thumbnails, metadata translations, filters, validation, and post assignment.
|
||||
- WordPress WXR migration with saved analyses, HTML-to-Markdown and shortcode conversion, conflict/taxonomy review, recoverable 500-item execution batches, media-parent linking, progress reporting, and optional AI-assisted taxonomy mapping.
|
||||
- Template and Lua script management with explicit syntax-check feedback, using a custom Ropey/Syntect/Cosmic Text editor and the documented, bDS2-signature-compatible project-scoped [`bds` host API](docs/scripting/API_REFERENCE.md) across utilities, rendered macros, and Blogmark transforms.
|
||||
- Template and Lua script management with explicit syntax-check feedback, using a custom Ropey/Syntect/Cosmic Text editor and the documented, bDS2-signature-compatible project-scoped [`bds` host API](docs/scripting/API_REFERENCE.md) across utilities, rendered macros, and Blogmark transforms, including airplane-gated Git sync.
|
||||
- SQLite and filesystem persistence with frontmatter, sidecars, rebuild, metadata diff/repair, and FTS5 search.
|
||||
- Optional on-device multilingual semantic search and tag suggestions backed by a persistent USearch index, with duplicate-post review and dismissal in the desktop workspace.
|
||||
- Read-only in-app browsers in the Help menu for the bundled global `DOCUMENTATION.md`, the generated Lua API reference with public types and runnable examples, the [CLI/server/TUI documentation](CLI.md), and the [MCP server documentation](MCP.md), with safe GFM rendering and confirmed external links.
|
||||
|
||||
@@ -335,47 +335,20 @@ fn active_project(db: &Database) -> Result<(Project, PathBuf)> {
|
||||
fn rebuild(db: &Database, incremental: bool) -> Result<CommandOutput> {
|
||||
let (project, data_dir) = active_project(db)?;
|
||||
if incremental {
|
||||
let (applied, imported, failed) = cli_sync::run_cli_mutation(db.conn(), || {
|
||||
let report =
|
||||
engine::metadata_diff::compute_metadata_diff(db.conn(), &data_dir, &project.id)?;
|
||||
if !report.errors.is_empty() {
|
||||
return Err(engine::EngineError::Validation(report.errors.join("; ")));
|
||||
}
|
||||
for item in &report.diffs {
|
||||
engine::metadata_diff::repair_metadata_diff_item(
|
||||
db.conn(),
|
||||
&data_dir,
|
||||
&project.id,
|
||||
engine::metadata_diff::RepairDirection::FileToDatabase,
|
||||
item,
|
||||
)?;
|
||||
}
|
||||
let filesystem_orphans = report
|
||||
.orphans
|
||||
.iter()
|
||||
.filter(|orphan| orphan.reason == "file_without_db_entry")
|
||||
.collect::<Vec<_>>();
|
||||
let mut imported = 0;
|
||||
let mut failed = 0;
|
||||
for orphan in filesystem_orphans {
|
||||
match engine::metadata_diff::import_orphan_file(
|
||||
db.conn(),
|
||||
&data_dir,
|
||||
&project.id,
|
||||
orphan,
|
||||
) {
|
||||
Ok(()) => imported += 1,
|
||||
Err(_) => failed += 1,
|
||||
}
|
||||
}
|
||||
if !report.diffs.is_empty() || imported > 0 {
|
||||
let report = cli_sync::run_cli_mutation(db.conn(), || {
|
||||
let report = engine::rebuild::rebuild_incremental(db.conn(), &data_dir, &project.id)?;
|
||||
if report.differences_applied > 0 || report.orphans_imported > 0 {
|
||||
emit_bulk(&project.id);
|
||||
}
|
||||
Ok((report.diffs.len(), imported, failed))
|
||||
Ok(report)
|
||||
})?;
|
||||
return Ok(output(
|
||||
"Applied incremental filesystem changes",
|
||||
json!({"differences_applied": applied, "orphans_imported": imported, "orphans_failed": failed}),
|
||||
json!({
|
||||
"differences_applied": report.differences_applied,
|
||||
"orphans_imported": report.orphans_imported,
|
||||
"orphans_failed": report.orphans_failed,
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -16,11 +16,10 @@ mod tests {
|
||||
use crate::db::Database;
|
||||
use crate::db::schema::{
|
||||
ai_catalog_meta, ai_endpoint_models, ai_model_modalities, ai_models, ai_providers,
|
||||
chat_conversations,
|
||||
chat_messages, db_notifications, dismissed_duplicate_pairs, embedding_keys,
|
||||
generated_file_hashes, import_definitions, mcp_proposals, media, media_translations,
|
||||
post_links, post_media, post_translations, posts, projects, scripts, settings, tags,
|
||||
templates,
|
||||
chat_conversations, chat_messages, db_notifications, dismissed_duplicate_pairs,
|
||||
embedding_keys, generated_file_hashes, import_definitions, mcp_proposals, media,
|
||||
media_translations, post_links, post_media, post_translations, posts, projects, scripts,
|
||||
settings, tags, templates,
|
||||
};
|
||||
use diesel::prelude::*;
|
||||
use diesel_migrations::MigrationHarness;
|
||||
|
||||
@@ -29,6 +29,51 @@ pub struct FullRebuildReport {
|
||||
pub errors: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq)]
|
||||
pub struct IncrementalRebuildReport {
|
||||
pub differences_applied: usize,
|
||||
pub orphans_imported: usize,
|
||||
pub orphans_failed: usize,
|
||||
}
|
||||
|
||||
/// Apply portable filesystem changes to one project's cache database.
|
||||
pub fn rebuild_incremental(
|
||||
conn: &Connection,
|
||||
data_dir: &Path,
|
||||
project_id: &str,
|
||||
) -> EngineResult<IncrementalRebuildReport> {
|
||||
let report = super::metadata_diff::compute_metadata_diff(conn, data_dir, project_id)?;
|
||||
if !report.errors.is_empty() {
|
||||
return Err(crate::engine::EngineError::Validation(
|
||||
report.errors.join("; "),
|
||||
));
|
||||
}
|
||||
for item in &report.diffs {
|
||||
super::metadata_diff::repair_metadata_diff_item(
|
||||
conn,
|
||||
data_dir,
|
||||
project_id,
|
||||
super::metadata_diff::RepairDirection::FileToDatabase,
|
||||
item,
|
||||
)?;
|
||||
}
|
||||
let mut result = IncrementalRebuildReport {
|
||||
differences_applied: report.diffs.len(),
|
||||
..Default::default()
|
||||
};
|
||||
for orphan in report
|
||||
.orphans
|
||||
.iter()
|
||||
.filter(|orphan| orphan.reason == "file_without_db_entry")
|
||||
{
|
||||
match super::metadata_diff::import_orphan_file(conn, data_dir, project_id, orphan) {
|
||||
Ok(()) => result.orphans_imported += 1,
|
||||
Err(_) => result.orphans_failed += 1,
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Progress callback: (percent 0.0..1.0, phase description).
|
||||
pub type ProgressFn = Arc<dyn Fn(f32, &str) + Send + Sync>;
|
||||
|
||||
|
||||
@@ -1239,6 +1239,64 @@ impl CoreHost {
|
||||
Ok(Value::Bool(true))
|
||||
}
|
||||
|
||||
fn sync(&self, method: &str, args: &[Value]) -> HostResult<Value> {
|
||||
if method == "check_availability" {
|
||||
return Ok(std::process::Command::new("git")
|
||||
.arg("--version")
|
||||
.output()
|
||||
.is_ok()
|
||||
.into());
|
||||
}
|
||||
if self.offline_mode && matches!(method, "fetch" | "pull" | "push") {
|
||||
return Err(format!("Git {method} is unavailable in airplane mode").into());
|
||||
}
|
||||
let git = engine::git::GitEngine::new(&self.data_dir);
|
||||
let cancelled = || {
|
||||
self.task_manager
|
||||
.as_ref()
|
||||
.zip(self.task_id)
|
||||
.is_some_and(|(manager, id)| manager.is_cancelled(id))
|
||||
};
|
||||
match method {
|
||||
"get_repo_state" | "get_remote_state" => {
|
||||
Ok(public_git_repository(git.repository().map_err(text)?))
|
||||
}
|
||||
"get_status" => Ok(json!({
|
||||
"files": git.status().map_err(text)?.into_iter().map(public_git_status).collect::<Vec<_>>(),
|
||||
})),
|
||||
"get_history" => {
|
||||
let repository = git.repository().map_err(text)?;
|
||||
let commits = match repository.current_branch {
|
||||
Some(branch) => git.history(&branch).map_err(text)?,
|
||||
None => Vec::new(),
|
||||
};
|
||||
Ok(json!({
|
||||
"commits": commits.into_iter().map(public_git_commit).collect::<Vec<_>>(),
|
||||
}))
|
||||
}
|
||||
"fetch" => {
|
||||
let result = git.fetch(cancelled, |_| {}).map_err(text)?;
|
||||
Ok(json!({"updated": true, "output": result.output}))
|
||||
}
|
||||
"pull" => {
|
||||
let result = git.pull(cancelled, |_| {}).map_err(text)?;
|
||||
let db = self.database()?;
|
||||
engine::rebuild::rebuild_incremental(db.conn(), &self.data_dir, &self.project_id)?;
|
||||
Ok(json!({"updated": true, "output": result.output}))
|
||||
}
|
||||
"push" => {
|
||||
let result = git.push(cancelled, |_| {}).map_err(text)?;
|
||||
Ok(json!({"updated": true, "output": result.output}))
|
||||
}
|
||||
"commit_all" => {
|
||||
let message = string_arg(args, 0)?;
|
||||
let result = git.commit_all(message).map_err(text)?;
|
||||
Ok(json!({"message": message, "output": result.output}))
|
||||
}
|
||||
_ => Err(format!("unknown sync capability: {method}").into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn publish(&self, method: &str, args: &[Value]) -> HostResult<Value> {
|
||||
if method != "upload_site" {
|
||||
return Err(format!("unknown publish capability: {method}").into());
|
||||
@@ -1492,6 +1550,7 @@ impl HostApi for CoreHost {
|
||||
"templates" => self.templates(method, &arguments),
|
||||
"tags" => self.tags(method, &arguments),
|
||||
"tasks" => self.tasks(method, &arguments),
|
||||
"sync" => self.sync(method, &arguments),
|
||||
"publish" => self.publish(method, &arguments),
|
||||
"chat" => self.chat(method, &arguments),
|
||||
"embeddings" => self.embeddings(method, &arguments),
|
||||
@@ -1587,6 +1646,54 @@ fn public_tasks(values: Vec<TaskSnapshot>) -> HostResult<Value> {
|
||||
.map(Value::Array)
|
||||
}
|
||||
|
||||
fn public_git_repository(value: engine::git::GitRepository) -> Value {
|
||||
json!({
|
||||
"is_initialized": value.is_initialized,
|
||||
"remote_url": value.remote_url,
|
||||
"provider": value.provider.map(|provider| json!({"kind": match provider {
|
||||
engine::git::GitProvider::GitHub => "github",
|
||||
engine::git::GitProvider::GitLab => "gitlab",
|
||||
engine::git::GitProvider::GiteaForgejo => "gitea_forgejo",
|
||||
}})),
|
||||
"current_branch": value.current_branch,
|
||||
"has_lfs": value.has_lfs,
|
||||
})
|
||||
}
|
||||
|
||||
fn public_git_status(value: engine::git::GitFileStatus) -> Value {
|
||||
let mut result = Map::new();
|
||||
result.insert("path".into(), value.path.into());
|
||||
if let Some(old_path) = value.old_path {
|
||||
result.insert("old_path".into(), old_path.into());
|
||||
}
|
||||
result.insert(
|
||||
"status".into(),
|
||||
match value.kind {
|
||||
engine::git::FileStatusKind::Added => "added",
|
||||
engine::git::FileStatusKind::Modified => "modified",
|
||||
engine::git::FileStatusKind::Deleted => "deleted",
|
||||
engine::git::FileStatusKind::Renamed => "renamed",
|
||||
engine::git::FileStatusKind::Untracked => "untracked",
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
Value::Object(result)
|
||||
}
|
||||
|
||||
fn public_git_commit(value: engine::git::GitCommit) -> Value {
|
||||
json!({
|
||||
"hash": value.hash,
|
||||
"subject": value.subject,
|
||||
"author": value.author,
|
||||
"date": value.date,
|
||||
"sync_status": {"kind": match value.sync_status {
|
||||
engine::git::SyncStatus::LocalOnly => "local_only",
|
||||
engine::git::SyncStatus::RemoteOnly => "remote_only",
|
||||
engine::git::SyncStatus::Both => "both",
|
||||
}},
|
||||
})
|
||||
}
|
||||
|
||||
fn public_metadata(data_dir: &Path) -> HostResult<Value> {
|
||||
let metadata = engine::meta::read_project_json(data_dir)?;
|
||||
Ok(json!({
|
||||
@@ -1862,6 +1969,255 @@ fn one_shot_json(value: engine::ai::OneShotResponse) -> HostResult<Value> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::scripting::{ExecutionControl, ExecutionKind, execute_with_host};
|
||||
use std::fs;
|
||||
use std::process::Command;
|
||||
|
||||
struct SyncFixture {
|
||||
_temp: tempfile::TempDir,
|
||||
db_path: PathBuf,
|
||||
data_dir: PathBuf,
|
||||
remote_dir: PathBuf,
|
||||
project_id: String,
|
||||
}
|
||||
|
||||
impl SyncFixture {
|
||||
fn new() -> Self {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let db_path = temp.path().join("ruds.db");
|
||||
let data_dir = temp.path().join("project");
|
||||
let remote_dir = temp.path().join("remote.git");
|
||||
let upstream_dir = temp.path().join("upstream");
|
||||
let db = Database::open(&db_path).unwrap();
|
||||
db.migrate().unwrap();
|
||||
crate::db::fts::ensure_fts_tables(db.conn()).unwrap();
|
||||
let project = engine::project::create_project(
|
||||
db.conn(),
|
||||
"Active",
|
||||
Some(data_dir.to_str().unwrap()),
|
||||
)
|
||||
.unwrap();
|
||||
engine::project::set_active_project(db.conn(), &project.id).unwrap();
|
||||
drop(db);
|
||||
|
||||
git(&data_dir, &["init", "-b", "master"]);
|
||||
configure_git(&data_dir);
|
||||
git(&data_dir, &["add", "-A"]);
|
||||
git(&data_dir, &["commit", "-m", "Initial project"]);
|
||||
git(
|
||||
temp.path(),
|
||||
&[
|
||||
"init",
|
||||
"--bare",
|
||||
"-b",
|
||||
"master",
|
||||
remote_dir.to_str().unwrap(),
|
||||
],
|
||||
);
|
||||
git(
|
||||
&data_dir,
|
||||
&["remote", "add", "origin", remote_dir.to_str().unwrap()],
|
||||
);
|
||||
git(&data_dir, &["push", "-u", "origin", "master"]);
|
||||
git(
|
||||
temp.path(),
|
||||
&[
|
||||
"clone",
|
||||
remote_dir.to_str().unwrap(),
|
||||
upstream_dir.to_str().unwrap(),
|
||||
],
|
||||
);
|
||||
configure_git(&upstream_dir);
|
||||
let mut metadata = engine::meta::read_project_json(&upstream_dir).unwrap();
|
||||
metadata.name = "Pulled Name".into();
|
||||
engine::meta::write_project_json(&upstream_dir, &metadata).unwrap();
|
||||
git(&upstream_dir, &["add", "-A"]);
|
||||
git(&upstream_dir, &["commit", "-m", "Update project name"]);
|
||||
git(&upstream_dir, &["push", "origin", "master"]);
|
||||
fs::write(data_dir.join("pending.txt"), "pending").unwrap();
|
||||
|
||||
Self {
|
||||
_temp: temp,
|
||||
db_path,
|
||||
data_dir,
|
||||
remote_dir,
|
||||
project_id: project.id,
|
||||
}
|
||||
}
|
||||
|
||||
fn host(&self, offline: bool) -> CoreHost {
|
||||
CoreHost::new(&self.db_path, &self.project_id, &self.data_dir)
|
||||
.with_offline_mode(offline)
|
||||
}
|
||||
}
|
||||
|
||||
fn configure_git(dir: &Path) {
|
||||
git(dir, &["config", "user.name", "Lua Test"]);
|
||||
git(dir, &["config", "user.email", "lua@example.invalid"]);
|
||||
}
|
||||
|
||||
fn git(dir: &Path, args: &[&str]) -> String {
|
||||
let output = Command::new("git")
|
||||
.args(args)
|
||||
.current_dir(dir)
|
||||
.env("GIT_TERMINAL_PROMPT", "0")
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(
|
||||
output.status.success(),
|
||||
"git {} failed: {}",
|
||||
args.join(" "),
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
String::from_utf8(output.stdout).unwrap().trim().to_owned()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sync_namespace_runs_every_bds2_method_and_reconciles_pull() {
|
||||
let fixture = SyncFixture::new();
|
||||
let result = execute_with_host(
|
||||
r#"
|
||||
function main()
|
||||
local status = bds.sync.get_status()
|
||||
local history = bds.sync.get_history()
|
||||
return {
|
||||
available = bds.sync.check_availability(),
|
||||
repo = bds.sync.get_repo_state(),
|
||||
remote = bds.sync.get_remote_state(),
|
||||
status = status,
|
||||
history = history,
|
||||
fetched = bds.sync.fetch(),
|
||||
pulled = bds.sync.pull(),
|
||||
committed = bds.sync.commit_all("Lua commit"),
|
||||
pushed = bds.sync.push(),
|
||||
}
|
||||
end
|
||||
"#,
|
||||
"main",
|
||||
&Value::Null,
|
||||
ExecutionKind::Utility,
|
||||
&ExecutionControl::default(),
|
||||
Arc::new(fixture.host(false)),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(result.value["available"], true);
|
||||
assert_eq!(result.value["repo"]["is_initialized"], true);
|
||||
assert_eq!(result.value["remote"], result.value["repo"]);
|
||||
assert!(
|
||||
result.value["status"]["files"]
|
||||
.as_array()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.any(|file| file == &json!({"path":"pending.txt","status":"untracked"}))
|
||||
);
|
||||
let commit = &result.value["history"]["commits"][0];
|
||||
assert!(commit["date"].as_str().is_some_and(|date| date.len() == 10));
|
||||
assert!(commit["sync_status"]["kind"].is_string());
|
||||
assert_eq!(result.value["fetched"]["updated"], true);
|
||||
assert_eq!(result.value["pulled"]["updated"], true);
|
||||
assert_eq!(result.value["committed"]["message"], "Lua commit");
|
||||
assert_eq!(result.value["pushed"]["updated"], true);
|
||||
|
||||
let db = Database::open(&fixture.db_path).unwrap();
|
||||
assert_eq!(
|
||||
project::get_project_by_id(db.conn(), &fixture.project_id)
|
||||
.unwrap()
|
||||
.name,
|
||||
"Pulled Name"
|
||||
);
|
||||
assert_eq!(
|
||||
git(&fixture.remote_dir, &["log", "-1", "--format=%s"]),
|
||||
"Lua commit"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sync_namespace_uses_nil_for_non_repository_failures() {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let data_dir = temp.path().join("not-a-repository");
|
||||
fs::create_dir(&data_dir).unwrap();
|
||||
let result = execute_with_host(
|
||||
r#"
|
||||
function main()
|
||||
return {
|
||||
repo = bds.sync.get_repo_state(),
|
||||
remote = bds.sync.get_remote_state(),
|
||||
status = bds.sync.get_status(),
|
||||
history = bds.sync.get_history(),
|
||||
fetched = bds.sync.fetch(),
|
||||
pulled = bds.sync.pull(),
|
||||
pushed = bds.sync.push(),
|
||||
committed = bds.sync.commit_all("no repository"),
|
||||
}
|
||||
end
|
||||
"#,
|
||||
"main",
|
||||
&Value::Null,
|
||||
ExecutionKind::Utility,
|
||||
&ExecutionControl::default(),
|
||||
Arc::new(CoreHost::new(
|
||||
temp.path().join("missing.db"),
|
||||
"p1",
|
||||
&data_dir,
|
||||
)),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(result.value["repo"]["is_initialized"], false);
|
||||
assert_eq!(result.value["remote"], result.value["repo"]);
|
||||
assert_eq!(result.value["history"], json!({"commits": []}));
|
||||
for key in ["status", "fetched", "pulled", "pushed", "committed"] {
|
||||
assert!(result.value[key].is_null(), "{key} was not nil");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sync_namespace_airplane_gate_prevents_network_commands_and_rejects_blank_commit() {
|
||||
let fixture = SyncFixture::new();
|
||||
git(&fixture.data_dir, &["add", "pending.txt"]);
|
||||
git(&fixture.data_dir, &["commit", "-m", "Local only"]);
|
||||
let local_head = git(&fixture.data_dir, &["rev-parse", "HEAD"]);
|
||||
let tracking_head = git(
|
||||
&fixture.data_dir,
|
||||
&["rev-parse", "refs/remotes/origin/master"],
|
||||
);
|
||||
let remote_head = git(&fixture.remote_dir, &["rev-parse", "refs/heads/master"]);
|
||||
|
||||
let result = execute_with_host(
|
||||
r#"
|
||||
function main()
|
||||
return {
|
||||
fetched = bds.sync.fetch(),
|
||||
pulled = bds.sync.pull(),
|
||||
pushed = bds.sync.push(),
|
||||
committed = bds.sync.commit_all(" "),
|
||||
}
|
||||
end
|
||||
"#,
|
||||
"main",
|
||||
&Value::Null,
|
||||
ExecutionKind::Utility,
|
||||
&ExecutionControl::default(),
|
||||
Arc::new(fixture.host(true)),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
for key in ["fetched", "pulled", "pushed", "committed"] {
|
||||
assert!(result.value[key].is_null(), "{key} was not nil");
|
||||
}
|
||||
assert_eq!(git(&fixture.data_dir, &["rev-parse", "HEAD"]), local_head);
|
||||
assert_eq!(
|
||||
git(
|
||||
&fixture.data_dir,
|
||||
&["rev-parse", "refs/remotes/origin/master"],
|
||||
),
|
||||
tracking_head
|
||||
);
|
||||
assert_eq!(
|
||||
git(&fixture.remote_dir, &["rev-parse", "refs/heads/master"]),
|
||||
remote_head
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn project_host_round_trips_engines_and_enforces_scope_and_failure_values() {
|
||||
|
||||
@@ -376,6 +376,23 @@ fn field_description(field: &str) -> &'static str {
|
||||
"slug" => "URL-safe record identifier.",
|
||||
"description" => "Human-readable description.",
|
||||
"status" => "Current lifecycle state.",
|
||||
"kind" => "Public enum value.",
|
||||
"path" => "Current repository-relative path.",
|
||||
"old_path" => "Previous repository-relative path for a rename.",
|
||||
"files" => "Changed repository paths.",
|
||||
"commits" => "Commit history entries.",
|
||||
"hash" => "Git object identifier.",
|
||||
"subject" => "Commit subject.",
|
||||
"author" => "Commit author.",
|
||||
"date" => "ISO-8601 commit date.",
|
||||
"sync_status" => "Local/remote presence classification.",
|
||||
"current_branch" => "Checked-out branch name.",
|
||||
"has_lfs" => "Whether Git LFS tracking is configured.",
|
||||
"is_initialized" => "Whether the project folder is a Git repository.",
|
||||
"provider" => "Hosting provider inferred from the remote URL.",
|
||||
"remote_url" => "Configured origin URL.",
|
||||
"output" => "Git command output.",
|
||||
"updated" => "Whether the Git network command completed successfully.",
|
||||
"progress" => "Completion value reported by the task.",
|
||||
"message" => "Latest user-facing task message.",
|
||||
"created_at" => "Creation timestamp.",
|
||||
@@ -403,7 +420,6 @@ fn field_description(field: &str) -> &'static str {
|
||||
"file_path" => "Stored media file path.",
|
||||
"alt" => "Alternative text for the media.",
|
||||
"caption" => "Media caption.",
|
||||
"kind" => "Script or template kind.",
|
||||
"entrypoint" => "Lua function invoked by the runtime.",
|
||||
"color" => "Optional display color.",
|
||||
"post_template_slug" => "Template selected for tagged posts.",
|
||||
|
||||
@@ -463,6 +463,7 @@ mod tests {
|
||||
"projects",
|
||||
"publish",
|
||||
"scripts",
|
||||
"sync",
|
||||
"tags",
|
||||
"tasks",
|
||||
"templates",
|
||||
@@ -473,7 +474,7 @@ mod tests {
|
||||
r#"
|
||||
function main()
|
||||
return {
|
||||
sync = bds.sync,
|
||||
sync = type(bds.sync),
|
||||
embeddings = type(bds.embeddings),
|
||||
report_progress = type(bds.report_progress),
|
||||
post_search = type(bds.posts.search),
|
||||
@@ -494,6 +495,7 @@ mod tests {
|
||||
"post_search": "function",
|
||||
"app_toast": "function",
|
||||
"embeddings": "table",
|
||||
"sync": "table",
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -515,6 +517,7 @@ mod tests {
|
||||
bds.tags.get_all(),
|
||||
bds.tasks.status_snapshot(),
|
||||
bds.publish.upload_site({}),
|
||||
bds.sync.check_availability(),
|
||||
bds.chat.detect_post_language("title", "body"),
|
||||
bds.embeddings.get_progress(),
|
||||
}
|
||||
@@ -528,8 +531,8 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(execution.value.as_array().unwrap().len(), 12);
|
||||
assert_eq!(host.0.lock().unwrap().len(), 12);
|
||||
assert_eq!(execution.value.as_array().unwrap().len(), 13);
|
||||
assert_eq!(host.0.lock().unwrap().len(), 13);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -352,7 +352,9 @@ fn user_guide_path() -> PathBuf {
|
||||
}
|
||||
|
||||
fn root_document_path(name: &str) -> PathBuf {
|
||||
Path::new(env!("CARGO_MANIFEST_DIR")).join("../..").join(name)
|
||||
Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("../..")
|
||||
.join(name)
|
||||
}
|
||||
|
||||
pub fn parse_document(source: &str) -> ParsedDocument {
|
||||
|
||||
@@ -39,6 +39,7 @@ Macro scripts expose `render(input, context)` and transform scripts expose `main
|
||||
- [`bds.projects`](#bdsprojects)
|
||||
- [`bds.publish`](#bdspublish)
|
||||
- [`bds.scripts`](#bdsscripts)
|
||||
- [`bds.sync`](#bdssync)
|
||||
- [`bds.tags`](#bdstags)
|
||||
- [`bds.tasks`](#bdstasks)
|
||||
- [`bds.templates`](#bdstemplates)
|
||||
@@ -3987,6 +3988,280 @@ local result = bds.scripts.rebuild_from_files()
|
||||
}
|
||||
```
|
||||
|
||||
## `bds.sync`
|
||||
|
||||
### `bds.sync.check_availability`
|
||||
|
||||
Return whether Git is available on the current machine.
|
||||
|
||||
**Signature**
|
||||
|
||||
```text
|
||||
bds.sync.check_availability() -> boolean
|
||||
```
|
||||
|
||||
**Parameters**
|
||||
|
||||
None.
|
||||
|
||||
**Returns**
|
||||
|
||||
`boolean`. `false` means the operation was rejected or failed.
|
||||
|
||||
**Example call**
|
||||
|
||||
```lua
|
||||
local result = bds.sync.check_availability()
|
||||
```
|
||||
|
||||
**Example response**
|
||||
|
||||
```lua
|
||||
true
|
||||
```
|
||||
|
||||
### `bds.sync.get_repo_state`
|
||||
|
||||
Return repository state for the active project using the GitRepositoryState shape.
|
||||
|
||||
**Signature**
|
||||
|
||||
```text
|
||||
bds.sync.get_repo_state() -> table | nil
|
||||
```
|
||||
|
||||
**Parameters**
|
||||
|
||||
None.
|
||||
|
||||
**Returns**
|
||||
|
||||
`table | nil`. `nil` means no value was available or the host operation failed.
|
||||
|
||||
**Example call**
|
||||
|
||||
```lua
|
||||
local result = bds.sync.get_repo_state()
|
||||
```
|
||||
|
||||
**Example response**
|
||||
|
||||
```lua
|
||||
{ key = "value" }
|
||||
```
|
||||
|
||||
### `bds.sync.get_status`
|
||||
|
||||
Return Git status for the active project using the GitStatusResult shape.
|
||||
|
||||
**Signature**
|
||||
|
||||
```text
|
||||
bds.sync.get_status() -> table | nil
|
||||
```
|
||||
|
||||
**Parameters**
|
||||
|
||||
None.
|
||||
|
||||
**Returns**
|
||||
|
||||
`table | nil`. `nil` means no value was available or the host operation failed.
|
||||
|
||||
**Example call**
|
||||
|
||||
```lua
|
||||
local result = bds.sync.get_status()
|
||||
```
|
||||
|
||||
**Example response**
|
||||
|
||||
```lua
|
||||
{ key = "value" }
|
||||
```
|
||||
|
||||
### `bds.sync.get_history`
|
||||
|
||||
Return commit history for the active project using the GitHistoryResult shape.
|
||||
|
||||
**Signature**
|
||||
|
||||
```text
|
||||
bds.sync.get_history() -> table | nil
|
||||
```
|
||||
|
||||
**Parameters**
|
||||
|
||||
None.
|
||||
|
||||
**Returns**
|
||||
|
||||
`table | nil`. `nil` means no value was available or the host operation failed.
|
||||
|
||||
**Example call**
|
||||
|
||||
```lua
|
||||
local result = bds.sync.get_history()
|
||||
```
|
||||
|
||||
**Example response**
|
||||
|
||||
```lua
|
||||
{ key = "value" }
|
||||
```
|
||||
|
||||
### `bds.sync.get_remote_state`
|
||||
|
||||
Return the GitRepositoryState for the active project, matching the bDS2 remote-state alias.
|
||||
|
||||
**Signature**
|
||||
|
||||
```text
|
||||
bds.sync.get_remote_state() -> table | nil
|
||||
```
|
||||
|
||||
**Parameters**
|
||||
|
||||
None.
|
||||
|
||||
**Returns**
|
||||
|
||||
`table | nil`. `nil` means no value was available or the host operation failed.
|
||||
|
||||
**Example call**
|
||||
|
||||
```lua
|
||||
local result = bds.sync.get_remote_state()
|
||||
```
|
||||
|
||||
**Example response**
|
||||
|
||||
```lua
|
||||
{ key = "value" }
|
||||
```
|
||||
|
||||
### `bds.sync.fetch`
|
||||
|
||||
Fetch and prune remote Git refs for the active project, returning GitNetworkResult; unavailable in airplane mode.
|
||||
|
||||
**Signature**
|
||||
|
||||
```text
|
||||
bds.sync.fetch() -> table | nil
|
||||
```
|
||||
|
||||
**Parameters**
|
||||
|
||||
None.
|
||||
|
||||
**Returns**
|
||||
|
||||
`table | nil`. `nil` means no value was available or the host operation failed.
|
||||
|
||||
**Example call**
|
||||
|
||||
```lua
|
||||
local result = bds.sync.fetch()
|
||||
```
|
||||
|
||||
**Example response**
|
||||
|
||||
```lua
|
||||
{ key = "value" }
|
||||
```
|
||||
|
||||
### `bds.sync.pull`
|
||||
|
||||
Fast-forward pull the active project, reconcile its cache database, and return GitNetworkResult; unavailable in airplane mode.
|
||||
|
||||
**Signature**
|
||||
|
||||
```text
|
||||
bds.sync.pull() -> table | nil
|
||||
```
|
||||
|
||||
**Parameters**
|
||||
|
||||
None.
|
||||
|
||||
**Returns**
|
||||
|
||||
`table | nil`. `nil` means no value was available or the host operation failed.
|
||||
|
||||
**Example call**
|
||||
|
||||
```lua
|
||||
local result = bds.sync.pull()
|
||||
```
|
||||
|
||||
**Example response**
|
||||
|
||||
```lua
|
||||
{ key = "value" }
|
||||
```
|
||||
|
||||
### `bds.sync.push`
|
||||
|
||||
Push the active project to its configured remote and return GitNetworkResult; unavailable in airplane mode.
|
||||
|
||||
**Signature**
|
||||
|
||||
```text
|
||||
bds.sync.push() -> table | nil
|
||||
```
|
||||
|
||||
**Parameters**
|
||||
|
||||
None.
|
||||
|
||||
**Returns**
|
||||
|
||||
`table | nil`. `nil` means no value was available or the host operation failed.
|
||||
|
||||
**Example call**
|
||||
|
||||
```lua
|
||||
local result = bds.sync.push()
|
||||
```
|
||||
|
||||
**Example response**
|
||||
|
||||
```lua
|
||||
{ key = "value" }
|
||||
```
|
||||
|
||||
### `bds.sync.commit_all`
|
||||
|
||||
Stage and commit every pending change in the active project, returning GitCommitResult.
|
||||
|
||||
**Signature**
|
||||
|
||||
```text
|
||||
bds.sync.commit_all(message: string) -> table | nil
|
||||
```
|
||||
|
||||
**Parameters**
|
||||
|
||||
| Name | Type | Required | Example |
|
||||
| --- | --- | --- | --- |
|
||||
| `message` | `string` | Yes | `"Working"` |
|
||||
|
||||
**Returns**
|
||||
|
||||
`table | nil`. `nil` means no value was available or the host operation failed.
|
||||
|
||||
**Example call**
|
||||
|
||||
```lua
|
||||
local result = bds.sync.commit_all("Working")
|
||||
```
|
||||
|
||||
**Example response**
|
||||
|
||||
```lua
|
||||
{ key = "value" }
|
||||
```
|
||||
|
||||
## `bds.tags`
|
||||
|
||||
### `bds.tags.create`
|
||||
|
||||
@@ -22,6 +22,15 @@ These are the public, JSON-compatible records returned by the Lua host API. They
|
||||
- [`TagData`](#tagdata)
|
||||
- [`TaskData`](#taskdata)
|
||||
- [`TaskStatus`](#taskstatus)
|
||||
- [`GitProviderData`](#gitproviderdata)
|
||||
- [`GitRepositoryState`](#gitrepositorystate)
|
||||
- [`GitFileStatus`](#gitfilestatus)
|
||||
- [`GitStatusResult`](#gitstatusresult)
|
||||
- [`GitSyncStatus`](#gitsyncstatus)
|
||||
- [`GitCommitData`](#gitcommitdata)
|
||||
- [`GitHistoryResult`](#githistoryresult)
|
||||
- [`GitNetworkResult`](#gitnetworkresult)
|
||||
- [`GitCommitResult`](#gitcommitresult)
|
||||
- [`ValidationResult`](#validationresult)
|
||||
|
||||
## `ProjectData`
|
||||
@@ -185,7 +194,7 @@ Lua script record.
|
||||
| `enabled` | `boolean` | Yes | Whether the record is enabled. |
|
||||
| `entrypoint` | `string` | Yes | Lua function invoked by the runtime. |
|
||||
| `id` | `string` | Yes | Stable record identifier. |
|
||||
| `kind` | `string` | Yes | Script or template kind. |
|
||||
| `kind` | `string` | Yes | Public enum value. |
|
||||
| `project_id` | `string` | Yes | Identifier of the owning project. |
|
||||
| `slug` | `string` | Yes | URL-safe record identifier. |
|
||||
| `status` | `string` | Yes | Current lifecycle state. |
|
||||
@@ -217,7 +226,7 @@ Template record for site rendering.
|
||||
| `created_at` | `ISO-8601 string` | Yes | Creation timestamp. |
|
||||
| `enabled` | `boolean` | Yes | Whether the record is enabled. |
|
||||
| `id` | `string` | Yes | Stable record identifier. |
|
||||
| `kind` | `string` | Yes | Script or template kind. |
|
||||
| `kind` | `string` | Yes | Public enum value. |
|
||||
| `project_id` | `string` | Yes | Identifier of the owning project. |
|
||||
| `slug` | `string` | Yes | URL-safe record identifier. |
|
||||
| `status` | `string` | Yes | Current lifecycle state. |
|
||||
@@ -306,6 +315,194 @@ Aggregate task status snapshot.
|
||||
| `running_count` | `integer` | Yes | Number of currently running tasks. |
|
||||
| `tasks` | `TaskData[]` | Yes | Tasks included in this status snapshot. |
|
||||
|
||||
## `GitProviderData`
|
||||
|
||||
Git hosting provider classification used by bDS2 repository state.
|
||||
|
||||
**Lua shape**
|
||||
|
||||
```lua
|
||||
{
|
||||
kind = "utility",
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Meaning |
|
||||
| --- | --- | --- | --- |
|
||||
| `kind` | `string` | Yes | Public enum value. |
|
||||
|
||||
## `GitRepositoryState`
|
||||
|
||||
Repository state returned by bds.sync.get_repo_state and get_remote_state.
|
||||
|
||||
**Lua shape**
|
||||
|
||||
```lua
|
||||
{
|
||||
current_branch = "example",
|
||||
has_lfs = true,
|
||||
is_initialized = true,
|
||||
provider = {
|
||||
kind = "utility",
|
||||
},
|
||||
remote_url = "example",
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Meaning |
|
||||
| --- | --- | --- | --- |
|
||||
| `current_branch` | `string \| nil` | No | Checked-out branch name. |
|
||||
| `has_lfs` | `boolean` | Yes | Whether Git LFS tracking is configured. |
|
||||
| `is_initialized` | `boolean` | Yes | Whether the project folder is a Git repository. |
|
||||
| `provider` | `GitProviderData \| nil` | No | Hosting provider inferred from the remote URL. |
|
||||
| `remote_url` | `string \| nil` | No | Configured origin URL. |
|
||||
|
||||
## `GitFileStatus`
|
||||
|
||||
One changed path in the active repository.
|
||||
|
||||
**Lua shape**
|
||||
|
||||
```lua
|
||||
{
|
||||
old_path = "example",
|
||||
path = "example",
|
||||
status = "draft",
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Meaning |
|
||||
| --- | --- | --- | --- |
|
||||
| `old_path` | `string \| nil` | No | Previous repository-relative path for a rename. |
|
||||
| `path` | `string` | Yes | Current repository-relative path. |
|
||||
| `status` | `string` | Yes | Current lifecycle state. |
|
||||
|
||||
## `GitStatusResult`
|
||||
|
||||
Repository working-tree status returned by bds.sync.get_status.
|
||||
|
||||
**Lua shape**
|
||||
|
||||
```lua
|
||||
{
|
||||
files = {
|
||||
{
|
||||
old_path = "example",
|
||||
path = "example",
|
||||
status = "draft",
|
||||
}
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Meaning |
|
||||
| --- | --- | --- | --- |
|
||||
| `files` | `GitFileStatus[]` | Yes | Changed repository paths. |
|
||||
|
||||
## `GitSyncStatus`
|
||||
|
||||
Whether a commit exists locally, remotely, or in both histories.
|
||||
|
||||
**Lua shape**
|
||||
|
||||
```lua
|
||||
{
|
||||
kind = "utility",
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Meaning |
|
||||
| --- | --- | --- | --- |
|
||||
| `kind` | `string` | Yes | Public enum value. |
|
||||
|
||||
## `GitCommitData`
|
||||
|
||||
Commit entry returned in bds.sync history.
|
||||
|
||||
**Lua shape**
|
||||
|
||||
```lua
|
||||
{
|
||||
author = "example",
|
||||
date = "2026-07-19T08:00:00Z",
|
||||
hash = "example",
|
||||
subject = "example",
|
||||
sync_status = {
|
||||
kind = "utility",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Meaning |
|
||||
| --- | --- | --- | --- |
|
||||
| `author` | `string \| nil` | No | Commit author. |
|
||||
| `date` | `ISO-8601 string \| nil` | No | ISO-8601 commit date. |
|
||||
| `hash` | `string` | Yes | Git object identifier. |
|
||||
| `subject` | `string \| nil` | No | Commit subject. |
|
||||
| `sync_status` | `GitSyncStatus` | Yes | Local/remote presence classification. |
|
||||
|
||||
## `GitHistoryResult`
|
||||
|
||||
Commit history returned by bds.sync.get_history.
|
||||
|
||||
**Lua shape**
|
||||
|
||||
```lua
|
||||
{
|
||||
commits = {
|
||||
{
|
||||
author = "example",
|
||||
date = "2026-07-19T08:00:00Z",
|
||||
hash = "example",
|
||||
subject = "example",
|
||||
sync_status = {
|
||||
kind = "utility",
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Meaning |
|
||||
| --- | --- | --- | --- |
|
||||
| `commits` | `GitCommitData[]` | Yes | Commit history entries. |
|
||||
|
||||
## `GitNetworkResult`
|
||||
|
||||
Result of a successful bds.sync network operation.
|
||||
|
||||
**Lua shape**
|
||||
|
||||
```lua
|
||||
{
|
||||
output = "example",
|
||||
updated = true,
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Meaning |
|
||||
| --- | --- | --- | --- |
|
||||
| `output` | `string` | Yes | Git command output. |
|
||||
| `updated` | `boolean` | Yes | Whether the Git network command completed successfully. |
|
||||
|
||||
## `GitCommitResult`
|
||||
|
||||
Result of successfully committing all pending repository changes.
|
||||
|
||||
**Lua shape**
|
||||
|
||||
```lua
|
||||
{
|
||||
message = "Working",
|
||||
output = "example",
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Meaning |
|
||||
| --- | --- | --- | --- |
|
||||
| `message` | `string` | Yes | Latest user-facing task message. |
|
||||
| `output` | `string` | Yes | Git command output. |
|
||||
|
||||
## `ValidationResult`
|
||||
|
||||
Template validation result.
|
||||
|
||||
@@ -1428,6 +1428,75 @@
|
||||
"params": [],
|
||||
"returns": "boolean"
|
||||
},
|
||||
{
|
||||
"description": "Return whether Git is available on the current machine.",
|
||||
"name": "check_availability",
|
||||
"namespace": "sync",
|
||||
"params": [],
|
||||
"returns": "boolean"
|
||||
},
|
||||
{
|
||||
"description": "Return repository state for the active project using the GitRepositoryState shape.",
|
||||
"name": "get_repo_state",
|
||||
"namespace": "sync",
|
||||
"params": [],
|
||||
"returns": "table | nil"
|
||||
},
|
||||
{
|
||||
"description": "Return Git status for the active project using the GitStatusResult shape.",
|
||||
"name": "get_status",
|
||||
"namespace": "sync",
|
||||
"params": [],
|
||||
"returns": "table | nil"
|
||||
},
|
||||
{
|
||||
"description": "Return commit history for the active project using the GitHistoryResult shape.",
|
||||
"name": "get_history",
|
||||
"namespace": "sync",
|
||||
"params": [],
|
||||
"returns": "table | nil"
|
||||
},
|
||||
{
|
||||
"description": "Return the GitRepositoryState for the active project, matching the bDS2 remote-state alias.",
|
||||
"name": "get_remote_state",
|
||||
"namespace": "sync",
|
||||
"params": [],
|
||||
"returns": "table | nil"
|
||||
},
|
||||
{
|
||||
"description": "Fetch and prune remote Git refs for the active project, returning GitNetworkResult; unavailable in airplane mode.",
|
||||
"name": "fetch",
|
||||
"namespace": "sync",
|
||||
"params": [],
|
||||
"returns": "table | nil"
|
||||
},
|
||||
{
|
||||
"description": "Fast-forward pull the active project, reconcile its cache database, and return GitNetworkResult; unavailable in airplane mode.",
|
||||
"name": "pull",
|
||||
"namespace": "sync",
|
||||
"params": [],
|
||||
"returns": "table | nil"
|
||||
},
|
||||
{
|
||||
"description": "Push the active project to its configured remote and return GitNetworkResult; unavailable in airplane mode.",
|
||||
"name": "push",
|
||||
"namespace": "sync",
|
||||
"params": [],
|
||||
"returns": "table | nil"
|
||||
},
|
||||
{
|
||||
"description": "Stage and commit every pending change in the active project, returning GitCommitResult.",
|
||||
"name": "commit_all",
|
||||
"namespace": "sync",
|
||||
"params": [
|
||||
{
|
||||
"name": "message",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"returns": "table | nil"
|
||||
},
|
||||
{
|
||||
"description": "Upload the rendered site using the provided publishing credentials.",
|
||||
"name": "upload_site",
|
||||
@@ -1782,6 +1851,81 @@
|
||||
},
|
||||
"name": "TaskStatus"
|
||||
},
|
||||
{
|
||||
"description": "Git hosting provider classification used by bDS2 repository state.",
|
||||
"fields": {
|
||||
"kind": "string"
|
||||
},
|
||||
"name": "GitProviderData"
|
||||
},
|
||||
{
|
||||
"description": "Repository state returned by bds.sync.get_repo_state and get_remote_state.",
|
||||
"fields": {
|
||||
"current_branch": "string | nil",
|
||||
"has_lfs": "boolean",
|
||||
"is_initialized": "boolean",
|
||||
"provider": "GitProviderData | nil",
|
||||
"remote_url": "string | nil"
|
||||
},
|
||||
"name": "GitRepositoryState"
|
||||
},
|
||||
{
|
||||
"description": "One changed path in the active repository.",
|
||||
"fields": {
|
||||
"old_path": "string | nil",
|
||||
"path": "string",
|
||||
"status": "string"
|
||||
},
|
||||
"name": "GitFileStatus"
|
||||
},
|
||||
{
|
||||
"description": "Repository working-tree status returned by bds.sync.get_status.",
|
||||
"fields": {
|
||||
"files": "GitFileStatus[]"
|
||||
},
|
||||
"name": "GitStatusResult"
|
||||
},
|
||||
{
|
||||
"description": "Whether a commit exists locally, remotely, or in both histories.",
|
||||
"fields": {
|
||||
"kind": "string"
|
||||
},
|
||||
"name": "GitSyncStatus"
|
||||
},
|
||||
{
|
||||
"description": "Commit entry returned in bds.sync history.",
|
||||
"fields": {
|
||||
"author": "string | nil",
|
||||
"date": "ISO-8601 string | nil",
|
||||
"hash": "string",
|
||||
"subject": "string | nil",
|
||||
"sync_status": "GitSyncStatus"
|
||||
},
|
||||
"name": "GitCommitData"
|
||||
},
|
||||
{
|
||||
"description": "Commit history returned by bds.sync.get_history.",
|
||||
"fields": {
|
||||
"commits": "GitCommitData[]"
|
||||
},
|
||||
"name": "GitHistoryResult"
|
||||
},
|
||||
{
|
||||
"description": "Result of a successful bds.sync network operation.",
|
||||
"fields": {
|
||||
"output": "string",
|
||||
"updated": "boolean"
|
||||
},
|
||||
"name": "GitNetworkResult"
|
||||
},
|
||||
{
|
||||
"description": "Result of successfully committing all pending repository changes.",
|
||||
"fields": {
|
||||
"message": "string",
|
||||
"output": "string"
|
||||
},
|
||||
"name": "GitCommitResult"
|
||||
},
|
||||
{
|
||||
"description": "Template validation result.",
|
||||
"fields": {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1546,6 +1546,84 @@
|
||||
"parameters": [],
|
||||
"returns": "boolean"
|
||||
},
|
||||
{
|
||||
"description": "Return whether Git is available on the current machine.",
|
||||
"detail": "bds.sync.check_availability() -> boolean",
|
||||
"insert_text": "bds.sync.check_availability()",
|
||||
"label": "bds.sync.check_availability",
|
||||
"parameters": [],
|
||||
"returns": "boolean"
|
||||
},
|
||||
{
|
||||
"description": "Return repository state for the active project using the GitRepositoryState shape.",
|
||||
"detail": "bds.sync.get_repo_state() -> table | nil",
|
||||
"insert_text": "bds.sync.get_repo_state()",
|
||||
"label": "bds.sync.get_repo_state",
|
||||
"parameters": [],
|
||||
"returns": "table | nil"
|
||||
},
|
||||
{
|
||||
"description": "Return Git status for the active project using the GitStatusResult shape.",
|
||||
"detail": "bds.sync.get_status() -> table | nil",
|
||||
"insert_text": "bds.sync.get_status()",
|
||||
"label": "bds.sync.get_status",
|
||||
"parameters": [],
|
||||
"returns": "table | nil"
|
||||
},
|
||||
{
|
||||
"description": "Return commit history for the active project using the GitHistoryResult shape.",
|
||||
"detail": "bds.sync.get_history() -> table | nil",
|
||||
"insert_text": "bds.sync.get_history()",
|
||||
"label": "bds.sync.get_history",
|
||||
"parameters": [],
|
||||
"returns": "table | nil"
|
||||
},
|
||||
{
|
||||
"description": "Return the GitRepositoryState for the active project, matching the bDS2 remote-state alias.",
|
||||
"detail": "bds.sync.get_remote_state() -> table | nil",
|
||||
"insert_text": "bds.sync.get_remote_state()",
|
||||
"label": "bds.sync.get_remote_state",
|
||||
"parameters": [],
|
||||
"returns": "table | nil"
|
||||
},
|
||||
{
|
||||
"description": "Fetch and prune remote Git refs for the active project, returning GitNetworkResult; unavailable in airplane mode.",
|
||||
"detail": "bds.sync.fetch() -> table | nil",
|
||||
"insert_text": "bds.sync.fetch()",
|
||||
"label": "bds.sync.fetch",
|
||||
"parameters": [],
|
||||
"returns": "table | nil"
|
||||
},
|
||||
{
|
||||
"description": "Fast-forward pull the active project, reconcile its cache database, and return GitNetworkResult; unavailable in airplane mode.",
|
||||
"detail": "bds.sync.pull() -> table | nil",
|
||||
"insert_text": "bds.sync.pull()",
|
||||
"label": "bds.sync.pull",
|
||||
"parameters": [],
|
||||
"returns": "table | nil"
|
||||
},
|
||||
{
|
||||
"description": "Push the active project to its configured remote and return GitNetworkResult; unavailable in airplane mode.",
|
||||
"detail": "bds.sync.push() -> table | nil",
|
||||
"insert_text": "bds.sync.push()",
|
||||
"label": "bds.sync.push",
|
||||
"parameters": [],
|
||||
"returns": "table | nil"
|
||||
},
|
||||
{
|
||||
"description": "Stage and commit every pending change in the active project, returning GitCommitResult.",
|
||||
"detail": "bds.sync.commit_all(message: string) -> table | nil",
|
||||
"insert_text": "bds.sync.commit_all(\"Working\")",
|
||||
"label": "bds.sync.commit_all",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "message",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
"returns": "table | nil"
|
||||
},
|
||||
{
|
||||
"description": "Upload the rendered site using the provided publishing credentials.",
|
||||
"detail": "bds.publish.upload_site(credentials: table) -> TaskData | nil",
|
||||
|
||||
@@ -135,6 +135,8 @@ surface ScriptRuntimeSurface {
|
||||
-- sync_from_posts.
|
||||
-- bds.tasks: get, status_snapshot, cancel, get_all, get_running,
|
||||
-- clear_completed.
|
||||
-- bds.sync: check_availability, get_repo_state, get_status,
|
||||
-- get_history, get_remote_state, fetch, pull, push, commit_all.
|
||||
-- bds.publish: upload_site.
|
||||
-- bds.chat: detect_post_language, analyze_post, translate_post,
|
||||
-- analyze_media_image, detect_media_language,
|
||||
@@ -144,7 +146,6 @@ surface ScriptRuntimeSurface {
|
||||
-- host may retain bds.app.progress and bds.app.toast as additive APIs.
|
||||
-- bds.embeddings: compute_similarities, dismiss_pair, find_duplicates,
|
||||
-- find_similar, get_progress, index_unindexed_posts, suggest_tags.
|
||||
-- bds.sync remains a future extension contract.
|
||||
|
||||
@guarantee HostApiFailureValues
|
||||
-- Host failures do not expose implementation exceptions across the Lua
|
||||
|
||||
Reference in New Issue
Block a user