diff --git a/crates/api/src/dto/pack.rs b/crates/api/src/dto/pack.rs index 5a4f993..f705819 100644 --- a/crates/api/src/dto/pack.rs +++ b/crates/api/src/dto/pack.rs @@ -48,10 +48,15 @@ pub struct CreatePackRequest { #[schema(example = json!(["messaging", "collaboration"]))] pub tags: Vec, - /// Runtime dependencies (refs of required packs) + /// Runtime dependencies (e.g., shell, python, nodejs) + #[serde(default)] + #[schema(example = json!(["shell", "python"]))] + pub runtime_deps: Vec, + + /// Pack dependencies (refs of required packs) #[serde(default)] #[schema(example = json!(["core"]))] - pub runtime_deps: Vec, + pub dependencies: Vec, /// Whether this is a standard/built-in pack #[serde(default)] @@ -152,10 +157,14 @@ pub struct UpdatePackRequest { #[schema(example = json!(["messaging", "collaboration", "webhooks"]))] pub tags: Option>, - /// Runtime dependencies - #[schema(example = json!(["core", "http"]))] + /// Runtime dependencies (e.g., shell, python, nodejs) + #[schema(example = json!(["shell", "python"]))] pub runtime_deps: Option>, + /// Pack dependencies (refs of required packs) + #[schema(example = json!(["core", "http"]))] + pub dependencies: Option>, + /// Whether this is a standard pack #[schema(example = false)] pub is_standard: Option, @@ -200,10 +209,14 @@ pub struct PackResponse { #[schema(example = json!(["messaging", "collaboration"]))] pub tags: Vec, - /// Runtime dependencies - #[schema(example = json!(["core"]))] + /// Runtime dependencies (e.g., shell, python, nodejs) + #[schema(example = json!(["shell", "python"]))] pub runtime_deps: Vec, + /// Pack dependencies (refs of required packs) + #[schema(example = json!(["core"]))] + pub dependencies: Vec, + /// Is standard pack #[schema(example = false)] pub is_standard: bool, @@ -271,6 +284,7 @@ impl From for PackResponse { meta: pack.meta, tags: pack.tags, runtime_deps: pack.runtime_deps, + dependencies: pack.dependencies, is_standard: pack.is_standard, created: pack.created, updated: pack.updated, @@ -803,6 +817,7 @@ mod tests { assert_eq!(req.version, "1.0.0"); assert!(req.tags.is_empty()); assert!(req.runtime_deps.is_empty()); + assert!(req.dependencies.is_empty()); assert!(!req.is_standard); } @@ -818,6 +833,7 @@ mod tests { meta: default_empty_object(), tags: vec![], runtime_deps: vec![], + dependencies: vec![], is_standard: false, }; diff --git a/crates/api/src/routes/packs.rs b/crates/api/src/routes/packs.rs index ae65b8f..262c87d 100644 --- a/crates/api/src/routes/packs.rs +++ b/crates/api/src/routes/packs.rs @@ -139,6 +139,7 @@ pub async fn create_pack( meta: request.meta, tags: request.tags, runtime_deps: request.runtime_deps, + dependencies: request.dependencies, is_standard: request.is_standard, installers: serde_json::json!({}), }; @@ -222,6 +223,7 @@ pub async fn update_pack( meta: request.meta, tags: request.tags, runtime_deps: request.runtime_deps, + dependencies: request.dependencies, is_standard: request.is_standard, installers: None, }; @@ -522,6 +524,15 @@ async fn register_pack_internal( }) .unwrap_or_default(), runtime_deps: pack_yaml + .get("runtime_deps") + .and_then(|v| v.as_sequence()) + .map(|seq| { + seq.iter() + .filter_map(|v| v.as_str().map(|s| s.to_string())) + .collect() + }) + .unwrap_or_default(), + dependencies: pack_yaml .get("dependencies") .and_then(|v| v.as_sequence()) .map(|seq| { diff --git a/crates/api/tests/helpers.rs b/crates/api/tests/helpers.rs index 5a602dd..aa5298b 100644 --- a/crates/api/tests/helpers.rs +++ b/crates/api/tests/helpers.rs @@ -433,6 +433,7 @@ pub async fn create_test_pack(pool: &PgPool, ref_name: &str) -> Result { }), tags: vec!["test".to_string()], runtime_deps: vec![], + dependencies: vec![], is_standard: false, installers: json!({}), }; diff --git a/crates/api/tests/sse_execution_stream_tests.rs b/crates/api/tests/sse_execution_stream_tests.rs index f57da86..b6b3770 100644 --- a/crates/api/tests/sse_execution_stream_tests.rs +++ b/crates/api/tests/sse_execution_stream_tests.rs @@ -41,6 +41,7 @@ async fn setup_test_pack_and_action(pool: &PgPool) -> Result<(Pack, Action)> { meta: json!({"author": "test"}), tags: vec!["test".to_string()], runtime_deps: vec![], + dependencies: vec![], is_standard: false, installers: json!({}), }; diff --git a/crates/api/tests/webhook_api_tests.rs b/crates/api/tests/webhook_api_tests.rs index 60b2198..e129a44 100644 --- a/crates/api/tests/webhook_api_tests.rs +++ b/crates/api/tests/webhook_api_tests.rs @@ -39,6 +39,7 @@ async fn create_test_pack(state: &AppState, name: &str) -> i64 { meta: serde_json::json!({}), tags: vec![], runtime_deps: vec![], + dependencies: vec![], is_standard: false, installers: json!({}), }; diff --git a/crates/api/tests/webhook_security_tests.rs b/crates/api/tests/webhook_security_tests.rs index bdf77ba..649713f 100644 --- a/crates/api/tests/webhook_security_tests.rs +++ b/crates/api/tests/webhook_security_tests.rs @@ -47,6 +47,7 @@ async fn create_test_pack(state: &AppState, name: &str) -> i64 { meta: serde_json::json!({}), tags: vec![], runtime_deps: vec![], + dependencies: vec![], is_standard: false, installers: json!({}), }; diff --git a/crates/cli/src/commands/pack_index.rs b/crates/cli/src/commands/pack_index.rs index 042a185..3a6a717 100644 --- a/crates/cli/src/commands/pack_index.rs +++ b/crates/cli/src/commands/pack_index.rs @@ -146,7 +146,7 @@ pub async fn handle_index_update( .unwrap_or_default(); let runtime_deps: Vec = pack_yaml - .get("dependencies") + .get("runtime_deps") .and_then(|v| v.as_sequence()) .map(|seq| { seq.iter() diff --git a/crates/common/src/models.rs b/crates/common/src/models.rs index cfa39aa..7d3bc7f 100644 --- a/crates/common/src/models.rs +++ b/crates/common/src/models.rs @@ -393,6 +393,7 @@ pub mod pack { pub meta: JsonDict, pub tags: Vec, pub runtime_deps: Vec, + pub dependencies: Vec, pub is_standard: bool, pub installers: JsonDict, // Installation metadata (nullable for non-installed packs) diff --git a/crates/common/src/repositories/pack.rs b/crates/common/src/repositories/pack.rs index ea76b1f..fd48726 100644 --- a/crates/common/src/repositories/pack.rs +++ b/crates/common/src/repositories/pack.rs @@ -31,6 +31,7 @@ pub struct CreatePackInput { pub meta: JsonDict, pub tags: Vec, pub runtime_deps: Vec, + pub dependencies: Vec, pub is_standard: bool, pub installers: JsonDict, } @@ -46,30 +47,24 @@ pub struct UpdatePackInput { pub meta: Option, pub tags: Option>, pub runtime_deps: Option>, + pub dependencies: Option>, pub is_standard: Option, pub installers: Option, } +const PACK_COLUMNS: &str = "id, ref, label, description, version, conf_schema, config, meta, tags, runtime_deps, dependencies, is_standard, installers, source_type, source_url, source_ref, checksum, checksum_verified, installed_at, installed_by, installation_method, storage_path, created, updated"; + #[async_trait::async_trait] impl FindById for PackRepository { async fn find_by_id<'e, E>(executor: E, id: i64) -> Result> where E: Executor<'e, Database = Postgres> + 'e, { - let pack = sqlx::query_as::<_, Pack>( - r#" - SELECT id, ref, label, description, version, conf_schema, config, meta, - tags, runtime_deps, is_standard, installers, - source_type, source_url, source_ref, checksum, checksum_verified, - installed_at, installed_by, installation_method, storage_path, - created, updated - FROM pack - WHERE id = $1 - "#, - ) - .bind(id) - .fetch_optional(executor) - .await?; + let query = format!("SELECT {} FROM pack WHERE id = $1", PACK_COLUMNS); + let pack = sqlx::query_as::<_, Pack>(&query) + .bind(id) + .fetch_optional(executor) + .await?; Ok(pack) } @@ -81,20 +76,11 @@ impl FindByRef for PackRepository { where E: Executor<'e, Database = Postgres> + 'e, { - let pack = sqlx::query_as::<_, Pack>( - r#" - SELECT id, ref, label, description, version, conf_schema, config, meta, - tags, runtime_deps, is_standard, installers, - source_type, source_url, source_ref, checksum, checksum_verified, - installed_at, installed_by, installation_method, storage_path, - created, updated - FROM pack - WHERE ref = $1 - "#, - ) - .bind(ref_str) - .fetch_optional(executor) - .await?; + let query = format!("SELECT {} FROM pack WHERE ref = $1", PACK_COLUMNS); + let pack = sqlx::query_as::<_, Pack>(&query) + .bind(ref_str) + .fetch_optional(executor) + .await?; Ok(pack) } @@ -106,19 +92,10 @@ impl List for PackRepository { where E: Executor<'e, Database = Postgres> + 'e, { - let packs = sqlx::query_as::<_, Pack>( - r#" - SELECT id, ref, label, description, version, conf_schema, config, meta, - tags, runtime_deps, is_standard, installers, - source_type, source_url, source_ref, checksum, checksum_verified, - installed_at, installed_by, installation_method, storage_path, - created, updated - FROM pack - ORDER BY ref ASC - "#, - ) - .fetch_all(executor) - .await?; + let query = format!("SELECT {} FROM pack ORDER BY ref ASC", PACK_COLUMNS); + let packs = sqlx::query_as::<_, Pack>(&query) + .fetch_all(executor) + .await?; Ok(packs) } @@ -143,41 +120,41 @@ impl Create for PackRepository { )); } - // Try to insert - database will enforce uniqueness constraint - let pack = sqlx::query_as::<_, Pack>( + let query = format!( r#" INSERT INTO pack (ref, label, description, version, conf_schema, config, meta, - tags, runtime_deps, is_standard, installers) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) - RETURNING id, ref, label, description, version, conf_schema, config, meta, - tags, runtime_deps, is_standard, installers, - source_type, source_url, source_ref, checksum, checksum_verified, - installed_at, installed_by, installation_method, storage_path, - created, updated + tags, runtime_deps, dependencies, is_standard, installers) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12) + RETURNING {} "#, - ) - .bind(&input.r#ref) - .bind(&input.label) - .bind(&input.description) - .bind(&input.version) - .bind(&input.conf_schema) - .bind(&input.config) - .bind(&input.meta) - .bind(&input.tags) - .bind(&input.runtime_deps) - .bind(input.is_standard) - .bind(&input.installers) - .fetch_one(executor) - .await - .map_err(|e| { - // Convert unique constraint violation to AlreadyExists error - if let sqlx::Error::Database(db_err) = &e { - if db_err.is_unique_violation() { - return Error::already_exists("Pack", "ref", &input.r#ref); + PACK_COLUMNS + ); + + // Try to insert - database will enforce uniqueness constraint + let pack = sqlx::query_as::<_, Pack>(&query) + .bind(&input.r#ref) + .bind(&input.label) + .bind(&input.description) + .bind(&input.version) + .bind(&input.conf_schema) + .bind(&input.config) + .bind(&input.meta) + .bind(&input.tags) + .bind(&input.runtime_deps) + .bind(&input.dependencies) + .bind(input.is_standard) + .bind(&input.installers) + .fetch_one(executor) + .await + .map_err(|e| { + // Convert unique constraint violation to AlreadyExists error + if let sqlx::Error::Database(db_err) = &e { + if db_err.is_unique_violation() { + return Error::already_exists("Pack", "ref", &input.r#ref); + } } - } - e.into() - })?; + e.into() + })?; Ok(pack) } @@ -267,6 +244,15 @@ impl Update for PackRepository { has_updates = true; } + if let Some(dependencies) = &input.dependencies { + if has_updates { + query.push(", "); + } + query.push("dependencies = "); + query.push_bind(dependencies); + has_updates = true; + } + if let Some(is_standard) = input.is_standard { if has_updates { query.push(", "); @@ -295,7 +281,8 @@ impl Update for PackRepository { // Add updated timestamp query.push(", updated = NOW() WHERE id = "); query.push_bind(id); - query.push(" RETURNING id, ref, label, description, version, conf_schema, config, meta, tags, runtime_deps, is_standard, installers, source_type, source_url, source_ref, checksum, checksum_verified, installed_at, installed_by, installation_method, storage_path, created, updated"); + query.push(" RETURNING "); + query.push(PACK_COLUMNS); let pack = query .build_query_as::() @@ -331,22 +318,15 @@ impl PackRepository { where E: Executor<'e, Database = Postgres> + 'e, { - let packs = sqlx::query_as::<_, Pack>( - r#" - SELECT id, ref, label, description, version, conf_schema, config, meta, - tags, runtime_deps, is_standard, installers, - source_type, source_url, source_ref, checksum, checksum_verified, - installed_at, installed_by, installation_method, storage_path, - created, updated - FROM pack - ORDER BY ref ASC - LIMIT $1 OFFSET $2 - "#, - ) - .bind(pagination.limit()) - .bind(pagination.offset()) - .fetch_all(executor) - .await?; + let query = format!( + "SELECT {} FROM pack ORDER BY ref ASC LIMIT $1 OFFSET $2", + PACK_COLUMNS + ); + let packs = sqlx::query_as::<_, Pack>(&query) + .bind(pagination.limit()) + .bind(pagination.offset()) + .fetch_all(executor) + .await?; Ok(packs) } @@ -368,21 +348,14 @@ impl PackRepository { where E: Executor<'e, Database = Postgres> + 'e, { - let packs = sqlx::query_as::<_, Pack>( - r#" - SELECT id, ref, label, description, version, conf_schema, config, meta, - tags, runtime_deps, is_standard, installers, - source_type, source_url, source_ref, checksum, checksum_verified, - installed_at, installed_by, installation_method, storage_path, - created, updated - FROM pack - WHERE $1 = ANY(tags) - ORDER BY ref ASC - "#, - ) - .bind(tag) - .fetch_all(executor) - .await?; + let query = format!( + "SELECT {} FROM pack WHERE $1 = ANY(tags) ORDER BY ref ASC", + PACK_COLUMNS + ); + let packs = sqlx::query_as::<_, Pack>(&query) + .bind(tag) + .fetch_all(executor) + .await?; Ok(packs) } @@ -392,20 +365,13 @@ impl PackRepository { where E: Executor<'e, Database = Postgres> + 'e, { - let packs = sqlx::query_as::<_, Pack>( - r#" - SELECT id, ref, label, description, version, conf_schema, config, meta, - tags, runtime_deps, is_standard, installers, - source_type, source_url, source_ref, checksum, checksum_verified, - installed_at, installed_by, installation_method, storage_path, - created, updated - FROM pack - WHERE is_standard = true - ORDER BY ref ASC - "#, - ) - .fetch_all(executor) - .await?; + let query = format!( + "SELECT {} FROM pack WHERE is_standard = true ORDER BY ref ASC", + PACK_COLUMNS + ); + let packs = sqlx::query_as::<_, Pack>(&query) + .fetch_all(executor) + .await?; Ok(packs) } @@ -416,21 +382,14 @@ impl PackRepository { E: Executor<'e, Database = Postgres> + 'e, { let search_pattern = format!("%{}%", query.to_lowercase()); - let packs = sqlx::query_as::<_, Pack>( - r#" - SELECT id, ref, label, description, version, conf_schema, config, meta, - tags, runtime_deps, is_standard, installers, - source_type, source_url, source_ref, checksum, checksum_verified, - installed_at, installed_by, installation_method, storage_path, - created, updated - FROM pack - WHERE LOWER(ref) LIKE $1 OR LOWER(label) LIKE $1 OR LOWER(description) LIKE $1 - ORDER BY ref ASC - "#, - ) - .bind(&search_pattern) - .fetch_all(executor) - .await?; + let sql = format!( + "SELECT {} FROM pack WHERE LOWER(ref) LIKE $1 OR LOWER(label) LIKE $1 OR LOWER(description) LIKE $1 ORDER BY ref ASC", + PACK_COLUMNS + ); + let packs = sqlx::query_as::<_, Pack>(&sql) + .bind(&search_pattern) + .fetch_all(executor) + .await?; Ok(packs) } @@ -464,7 +423,7 @@ impl PackRepository { where E: Executor<'e, Database = Postgres> + 'e, { - let pack = sqlx::query_as::<_, Pack>( + let query = format!( r#" UPDATE pack SET source_type = $2, @@ -478,28 +437,26 @@ impl PackRepository { storage_path = $9, updated = NOW() WHERE id = $1 - RETURNING id, ref, label, description, version, conf_schema, config, meta, - tags, runtime_deps, is_standard, installers, - source_type, source_url, source_ref, checksum, checksum_verified, - installed_at, installed_by, installation_method, storage_path, - created, updated + RETURNING {} "#, - ) - .bind(id) - .bind(source_type) - .bind(source_url) - .bind(source_ref) - .bind(checksum) - .bind(checksum_verified) - .bind(installed_by) - .bind(installation_method) - .bind(storage_path) - .fetch_one(executor) - .await - .map_err(|e| match e { - sqlx::Error::RowNotFound => Error::not_found("pack", "id", id.to_string()), - _ => e.into(), - })?; + PACK_COLUMNS + ); + let pack = sqlx::query_as::<_, Pack>(&query) + .bind(id) + .bind(source_type) + .bind(source_url) + .bind(source_ref) + .bind(checksum) + .bind(checksum_verified) + .bind(installed_by) + .bind(installation_method) + .bind(storage_path) + .fetch_one(executor) + .await + .map_err(|e| match e { + sqlx::Error::RowNotFound => Error::not_found("pack", "id", id.to_string()), + _ => e.into(), + })?; Ok(pack) } @@ -524,20 +481,13 @@ impl PackRepository { where E: Executor<'e, Database = Postgres> + 'e, { - let packs = sqlx::query_as::<_, Pack>( - r#" - SELECT id, ref, label, description, version, conf_schema, config, meta, - tags, runtime_deps, is_standard, installers, - source_type, source_url, source_ref, checksum, checksum_verified, - installed_at, installed_by, installation_method, storage_path, - created, updated - FROM pack - WHERE installed_at IS NOT NULL - ORDER BY installed_at DESC - "#, - ) - .fetch_all(executor) - .await?; + let query = format!( + "SELECT {} FROM pack WHERE installed_at IS NOT NULL ORDER BY installed_at DESC", + PACK_COLUMNS + ); + let packs = sqlx::query_as::<_, Pack>(&query) + .fetch_all(executor) + .await?; Ok(packs) } @@ -547,21 +497,14 @@ impl PackRepository { where E: Executor<'e, Database = Postgres> + 'e, { - let packs = sqlx::query_as::<_, Pack>( - r#" - SELECT id, ref, label, description, version, conf_schema, config, meta, - tags, runtime_deps, is_standard, installers, - source_type, source_url, source_ref, checksum, checksum_verified, - installed_at, installed_by, installation_method, storage_path, - created, updated - FROM pack - WHERE source_type = $1 - ORDER BY installed_at DESC - "#, - ) - .bind(source_type) - .fetch_all(executor) - .await?; + let query = format!( + "SELECT {} FROM pack WHERE source_type = $1 ORDER BY installed_at DESC", + PACK_COLUMNS + ); + let packs = sqlx::query_as::<_, Pack>(&query) + .bind(source_type) + .fetch_all(executor) + .await?; Ok(packs) } @@ -583,6 +526,7 @@ mod tests { meta: serde_json::json!({}), tags: vec!["test".to_string()], runtime_deps: vec![], + dependencies: vec![], is_standard: false, installers: serde_json::json!({}), }; @@ -597,5 +541,6 @@ mod tests { assert!(input.label.is_none()); assert!(input.description.is_none()); assert!(input.version.is_none()); + assert!(input.dependencies.is_none()); } } diff --git a/crates/common/tests/helpers.rs b/crates/common/tests/helpers.rs index dcc7ba2..e9c06d8 100644 --- a/crates/common/tests/helpers.rs +++ b/crates/common/tests/helpers.rs @@ -284,6 +284,7 @@ pub struct PackFixture { pub meta: serde_json::Value, pub tags: Vec, pub runtime_deps: Vec, + pub dependencies: Vec, pub is_standard: bool, } @@ -300,6 +301,7 @@ impl PackFixture { meta: json!({}), tags: vec!["test".to_string()], runtime_deps: vec![], + dependencies: vec![], is_standard: false, } } @@ -319,6 +321,7 @@ impl PackFixture { meta: json!({}), tags: vec!["test".to_string()], runtime_deps: vec![], + dependencies: vec![], is_standard: false, } } @@ -359,6 +362,7 @@ impl PackFixture { meta: self.meta, tags: self.tags, runtime_deps: self.runtime_deps, + dependencies: self.dependencies, is_standard: self.is_standard, installers: serde_json::json!({}), }; diff --git a/crates/common/tests/migration_tests.rs b/crates/common/tests/migration_tests.rs index df412ee..8770c1b 100644 --- a/crates/common/tests/migration_tests.rs +++ b/crates/common/tests/migration_tests.rs @@ -358,6 +358,7 @@ async fn test_pack_columns() { "conf_schema", "config", "created", + "dependencies", "description", "id", "is_standard", diff --git a/crates/common/tests/pack_repository_tests.rs b/crates/common/tests/pack_repository_tests.rs index bd34ee6..7f774fe 100644 --- a/crates/common/tests/pack_repository_tests.rs +++ b/crates/common/tests/pack_repository_tests.rs @@ -392,6 +392,7 @@ async fn test_pack_transaction_commit() { meta: json!({}), tags: vec![], runtime_deps: vec![], + dependencies: vec![], is_standard: false, installers: json!({}), }; @@ -428,6 +429,7 @@ async fn test_pack_transaction_rollback() { meta: json!({}), tags: vec![], runtime_deps: vec![], + dependencies: vec![], is_standard: false, installers: json!({}), }; @@ -457,6 +459,7 @@ async fn test_pack_invalid_ref_format() { meta: json!({}), tags: vec![], runtime_deps: vec![], + dependencies: vec![], is_standard: false, installers: json!({}), }; @@ -491,8 +494,9 @@ async fn test_pack_valid_ref_formats() { meta: json!({}), tags: vec![], runtime_deps: vec![], + dependencies: vec![], is_standard: false, - installers: json!({}), + installers: json!({}), }; let result = PackRepository::create(&pool, input).await; diff --git a/crates/common/tests/permission_repository_tests.rs b/crates/common/tests/permission_repository_tests.rs index aceb754..fb4c4b5 100644 --- a/crates/common/tests/permission_repository_tests.rs +++ b/crates/common/tests/permission_repository_tests.rs @@ -79,8 +79,9 @@ impl PermissionSetFixture { config: json!({}), meta: json!({}), runtime_deps: vec![], + dependencies: vec![], is_standard: false, - installers: json!({}), + installers: json!({}), }; PackRepository::create(&self.pool, input) .await @@ -95,7 +96,7 @@ impl PermissionSetFixture { login, display_name: Some("Test User".to_string()), attributes: json!({}), - password_hash: None, + password_hash: None, }; IdentityRepository::create(&self.pool, input) .await diff --git a/crates/common/tests/repository_runtime_tests.rs b/crates/common/tests/repository_runtime_tests.rs index cb0a16f..51f04d7 100644 --- a/crates/common/tests/repository_runtime_tests.rs +++ b/crates/common/tests/repository_runtime_tests.rs @@ -377,6 +377,7 @@ async fn test_find_by_pack() { }), tags: vec!["test".to_string()], runtime_deps: vec![], + dependencies: vec![], is_standard: false, installers: json!({}), }; diff --git a/crates/executor/tests/fifo_ordering_integration_test.rs b/crates/executor/tests/fifo_ordering_integration_test.rs index b2f46ad..04e0758 100644 --- a/crates/executor/tests/fifo_ordering_integration_test.rs +++ b/crates/executor/tests/fifo_ordering_integration_test.rs @@ -52,6 +52,7 @@ async fn create_test_pack(pool: &PgPool, suffix: &str) -> i64 { meta: json!({}), tags: vec![], runtime_deps: vec![], + dependencies: vec![], is_standard: false, installers: json!({}), }; diff --git a/crates/executor/tests/policy_enforcer_tests.rs b/crates/executor/tests/policy_enforcer_tests.rs index b89ec93..aa197f3 100644 --- a/crates/executor/tests/policy_enforcer_tests.rs +++ b/crates/executor/tests/policy_enforcer_tests.rs @@ -45,6 +45,7 @@ async fn create_test_pack(pool: &PgPool, suffix: &str) -> i64 { meta: json!({}), tags: vec![], runtime_deps: vec![], + dependencies: vec![], is_standard: false, installers: json!({}), }; diff --git a/migrations/20250101000002_pack_system.sql b/migrations/20250101000002_pack_system.sql index f0af0d0..99f522f 100644 --- a/migrations/20250101000002_pack_system.sql +++ b/migrations/20250101000002_pack_system.sql @@ -17,6 +17,7 @@ CREATE TABLE pack ( meta JSONB NOT NULL DEFAULT '{}'::jsonb, tags TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[], runtime_deps TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[], + dependencies TEXT[] NOT NULL DEFAULT ARRAY[]::TEXT[], is_standard BOOLEAN NOT NULL DEFAULT FALSE, installers JSONB DEFAULT '[]'::jsonb, @@ -52,6 +53,7 @@ CREATE INDEX idx_pack_config_gin ON pack USING GIN (config); CREATE INDEX idx_pack_meta_gin ON pack USING GIN (meta); CREATE INDEX idx_pack_tags_gin ON pack USING GIN (tags); CREATE INDEX idx_pack_runtime_deps_gin ON pack USING GIN (runtime_deps); +CREATE INDEX idx_pack_dependencies_gin ON pack USING GIN (dependencies); CREATE INDEX idx_pack_installed_at ON pack(installed_at DESC) WHERE installed_at IS NOT NULL; CREATE INDEX idx_pack_installed_by ON pack(installed_by) WHERE installed_by IS NOT NULL; CREATE INDEX idx_pack_source_type ON pack(source_type) WHERE source_type IS NOT NULL; @@ -70,7 +72,8 @@ COMMENT ON COLUMN pack.version IS 'Semantic version of the pack'; COMMENT ON COLUMN pack.conf_schema IS 'JSON schema for pack configuration'; COMMENT ON COLUMN pack.config IS 'Pack configuration values'; COMMENT ON COLUMN pack.meta IS 'Pack metadata'; -COMMENT ON COLUMN pack.runtime_deps IS 'Array of required runtime references'; +COMMENT ON COLUMN pack.runtime_deps IS 'Array of required runtime references (e.g., shell, python, nodejs)'; +COMMENT ON COLUMN pack.dependencies IS 'Array of required pack references (e.g., core, utils)'; COMMENT ON COLUMN pack.is_standard IS 'Whether this is a core/built-in pack'; COMMENT ON COLUMN pack.source_type IS 'Installation source type (e.g., "git", "local", "registry")'; COMMENT ON COLUMN pack.source_url IS 'URL or path where pack was installed from'; diff --git a/web/src/components/forms/PackForm.tsx b/web/src/components/forms/PackForm.tsx index 773997e..8557e88 100644 --- a/web/src/components/forms/PackForm.tsx +++ b/web/src/components/forms/PackForm.tsx @@ -31,9 +31,7 @@ export default function PackForm({ pack, onSuccess, onCancel }: PackFormProps) { const [description, setDescription] = useState(pack?.description || ""); const [version, setVersion] = useState(pack?.version || "1.0.0"); const [tags, setTags] = useState(pack?.tags?.join(", ") || ""); - const [runtimeDeps, setRuntimeDeps] = useState( - pack?.runtime_deps?.join(", ") || "", - ); + const [deps, setDeps] = useState(pack?.dependencies?.join(", ") || ""); const [isStandard, setIsStandard] = useState(pack?.is_standard ?? false); const [configValues, setConfigValues] = @@ -134,7 +132,7 @@ export default function PackForm({ pack, onSuccess, onCancel }: PackFormProps) { .split(",") .map((t) => t.trim()) .filter((t) => t); - const runtimeDepsList = runtimeDeps + const depsList = deps .split(",") .map((d) => d.trim()) .filter((d) => d); @@ -149,7 +147,7 @@ export default function PackForm({ pack, onSuccess, onCancel }: PackFormProps) { config: configValues, meta: parsedMeta, tags: tagsList, - runtime_deps: runtimeDepsList, + dependencies: depsList, is_standard: isStandard, }; await updatePack.mutateAsync({ ref: pack!.ref, data: updateData }); @@ -166,7 +164,7 @@ export default function PackForm({ pack, onSuccess, onCancel }: PackFormProps) { config: configValues, meta: parsedMeta, tags: tagsList, - runtime_deps: runtimeDepsList, + dependencies: depsList, is_standard: isStandard, }; const newPackResponse = await createPack.mutateAsync(createData); @@ -443,19 +441,19 @@ export default function PackForm({ pack, onSuccess, onCancel }: PackFormProps) {

- {/* Runtime Dependencies */} + {/* Pack Dependencies */}
setRuntimeDeps(e.target.value)} + id="deps" + value={deps} + onChange={(e) => setDeps(e.target.value)} disabled={isStandard} className={`w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 ${ isStandard ? "bg-gray-100 cursor-not-allowed" : "" @@ -463,7 +461,8 @@ export default function PackForm({ pack, onSuccess, onCancel }: PackFormProps) { placeholder="e.g., core, utils" />

- Comma-separated list of required pack refs + Comma-separated list of required pack refs (other packs this pack + depends on)