From 81ffa814d9b90b7a6c3770e6835783ef0c701431 Mon Sep 17 00:00:00 2001 From: Krishna Mohan Date: Tue, 27 Jan 2026 17:06:17 +0530 Subject: [PATCH 1/5] feat: added the feature write the name of artifact with custom and dynamic inputs Signed-off-by: Krishna Mohan --- .../src/components/workflow/ConfigPanel.tsx | 42 +++++-- .../workflow/DynamicArtifactNameInput.tsx | 117 ++++++++++++++++++ .../core/__tests__/artifact-writer.test.ts | 102 ++++++++++++++- worker/src/components/core/artifact-writer.ts | 88 +++++++++++-- 4 files changed, 322 insertions(+), 27 deletions(-) create mode 100644 frontend/src/components/workflow/DynamicArtifactNameInput.tsx diff --git a/frontend/src/components/workflow/ConfigPanel.tsx b/frontend/src/components/workflow/ConfigPanel.tsx index dbdc174f..ca6fd0f8 100644 --- a/frontend/src/components/workflow/ConfigPanel.tsx +++ b/frontend/src/components/workflow/ConfigPanel.tsx @@ -31,6 +31,7 @@ import { useComponentStore } from '@/store/componentStore'; import { ParameterFieldWrapper } from './ParameterField'; import { WebhookDetails } from './WebhookDetails'; import { SecretSelect } from '@/components/inputs/SecretSelect'; +import { DynamicArtifactNameInput } from './DynamicArtifactNameInput'; import type { Node } from 'reactflow'; import type { FrontendNodeData } from '@/schemas/node'; import type { ComponentType, KeyboardEvent } from 'react'; @@ -913,6 +914,20 @@ export function ConfigPanel({ placeholder={manualPlaceholder} onChange={(value) => handleInputOverrideChange(input.id, value)} /> + ) : component?.id === 'core.artifact.writer' && + input.id === 'artifactName' ? ( + { + if (!value || value === '') { + handleInputOverrideChange(input.id, undefined); + } else { + handleInputOverrideChange(input.id, value); + } + }} + disabled={manualLocked} + placeholder="{{run_id}}-{{timestamp}}" + /> ) : ( )} - {manualLocked ? ( -

- Disconnect the port to edit manual input. -

- ) : ( -

- {isBooleanInput - ? 'Select a value or clear manual input to require a port connection.' - : isListOfTextInput - ? 'Add entries or clear manual input to require a port connection.' - : 'Leave blank to require a port connection.'} -

+ {/* Skip helper text for DynamicArtifactNameInput as it has its own */} + {!(component?.id === 'core.artifact.writer' && input.id === 'artifactName') && ( + manualLocked ? ( +

+ Disconnect the port to edit manual input. +

+ ) : ( +

+ {isBooleanInput + ? 'Select a value or clear manual input to require a port connection.' + : isListOfTextInput + ? 'Add entries or clear manual input to require a port connection.' + : 'Leave blank to require a port connection.'} +

+ ) )} )} diff --git a/frontend/src/components/workflow/DynamicArtifactNameInput.tsx b/frontend/src/components/workflow/DynamicArtifactNameInput.tsx new file mode 100644 index 00000000..d179fc64 --- /dev/null +++ b/frontend/src/components/workflow/DynamicArtifactNameInput.tsx @@ -0,0 +1,117 @@ +import { useState } from 'react'; +import { ChevronDown, ChevronRight, Plus } from 'lucide-react'; +import { Input } from '@/components/ui/input'; +import { cn } from '@/lib/utils'; + +interface DynamicParameter { + placeholder: string; + description: string; +} + +const DYNAMIC_PARAMETERS: DynamicParameter[] = [ + { placeholder: '{{timestamp}}', description: 'Current timestamp' }, + { placeholder: '{{run_id}}', description: 'Run ID' }, + { placeholder: '{{dataset}}', description: 'Dataset name' }, + { placeholder: '{{task}}', description: 'Task name' }, + { placeholder: '{{run_name}}', description: 'Run name' }, + { placeholder: '{{date}}', description: 'Date (YYYY-MM-DD)' }, + { placeholder: '{{time}}', description: 'Time (HH-MM-SS)' }, +]; + +interface DynamicArtifactNameInputProps { + value: string; + onChange: (value: string) => void; + disabled?: boolean; + placeholder?: string; +} + +export function DynamicArtifactNameInput({ + value, + onChange, + disabled = false, + placeholder = '{{run_id}}-{{timestamp}}', +}: DynamicArtifactNameInputProps) { + const [isParamsOpen, setIsParamsOpen] = useState(true); + const currentValue = value || ''; + + const handleInsertPlaceholder = (placeholder: string) => { + if (disabled) return; + // Insert at the end of current value + const newValue = currentValue ? `${currentValue}${placeholder}` : placeholder; + onChange(newValue); + }; + + return ( +
+ onChange(e.target.value)} + placeholder={placeholder} + className="text-sm font-mono" + disabled={disabled} + /> +

+ e.g., task123-1617181920 +

+ + {/* Dynamic Parameters Section */} +
+ + + {isParamsOpen && ( +
+

+ These can be used in the name for the artifact. +

+
+ {DYNAMIC_PARAMETERS.map((param) => ( + + ))} +
+
+ )} +
+
+ ); +} diff --git a/worker/src/components/core/__tests__/artifact-writer.test.ts b/worker/src/components/core/__tests__/artifact-writer.test.ts index c05f6e25..c8740d58 100644 --- a/worker/src/components/core/__tests__/artifact-writer.test.ts +++ b/worker/src/components/core/__tests__/artifact-writer.test.ts @@ -27,7 +27,7 @@ describe('core.artifact.writer component', () => { const uploadMock = vi.fn().mockResolvedValue({ artifactId: 'artifact-123', fileId: 'file-123', - name: 'playground-artifact.txt', + name: 'run-log.txt', destinations: ['run', 'library'], }); @@ -44,10 +44,11 @@ describe('core.artifact.writer component', () => { const executePayload = { inputs: { + artifactName: 'run-log', content: 'Hello artifacts!', }, params: { - fileName: 'run-log.txt', + fileExtension: '.txt', mimeType: 'text/plain', saveToRunArtifacts: true, publishToArtifactLibrary: true, @@ -65,16 +66,61 @@ describe('core.artifact.writer component', () => { expect(result.saved).toBe(true); expect(result.artifactId).toBe('artifact-123'); + expect(result.artifactName).toBe('run-log'); + expect(result.fileName).toBe('run-log.txt'); expect(result.destinations).toEqual(['run', 'library']); }); + it('substitutes dynamic placeholders in artifact name', async () => { + if (!component) throw new Error('Component not registered'); + + const uploadMock = vi.fn().mockResolvedValue({ + artifactId: 'artifact-456', + fileId: 'file-456', + name: 'test-artifact.json', + destinations: ['run'], + }); + + const mockArtifacts: IArtifactService = { + upload: uploadMock, + download: vi.fn(), + }; + + const context = createExecutionContext({ + runId: 'test-run-abc123', + componentRef: 'artifact-writer-2', + artifacts: mockArtifacts, + }); + + const executePayload = { + inputs: { + artifactName: '{{run_id}}-{{task}}', + content: { data: 'test' }, + }, + params: { + fileExtension: '.json', + mimeType: 'application/json', + saveToRunArtifacts: true, + publishToArtifactLibrary: false, + }, + }; + + const result = await component.execute(executePayload, context); + + expect(uploadMock).toHaveBeenCalledTimes(1); + const payload = uploadMock.mock.calls[0][0]; + expect(payload.name).toBe('test-run-abc123-artifact-writer-2.json'); + expect(result.artifactName).toBe('test-run-abc123-artifact-writer-2'); + expect(result.fileName).toBe('test-run-abc123-artifact-writer-2.json'); + }); + it('skips upload when no destinations are selected', async () => { if (!component) throw new Error('Component not registered'); const uploadMock = vi.fn(); const context = createExecutionContext({ runId: 'run-2', - componentRef: 'artifact-writer-2', + componentRef: 'artifact-writer-skip', artifacts: { upload: uploadMock, download: vi.fn(), @@ -83,10 +129,11 @@ describe('core.artifact.writer component', () => { const executePayload = { inputs: { + artifactName: 'noop', content: 'No destinations', }, params: { - fileName: 'noop.txt', + fileExtension: '.txt', saveToRunArtifacts: false, publishToArtifactLibrary: false, }, @@ -97,6 +144,8 @@ describe('core.artifact.writer component', () => { expect(uploadMock).not.toHaveBeenCalled(); expect(result.saved).toBe(false); expect(result.artifactId).toBeUndefined(); + expect(result.artifactName).toBe('noop'); + expect(result.fileName).toBe('noop.txt'); expect(result.destinations).toEqual([]); }); @@ -110,9 +159,11 @@ describe('core.artifact.writer component', () => { const executePayload = { inputs: { + artifactName: 'test-artifact', content: 'Need artifacts', }, params: { + fileExtension: '.txt', saveToRunArtifacts: true, publishToArtifactLibrary: false, }, @@ -122,4 +173,47 @@ describe('core.artifact.writer component', () => { 'Artifact service is not available', ); }); + + it('uses default artifact name template when not provided', async () => { + if (!component) throw new Error('Component not registered'); + + const uploadMock = vi.fn().mockResolvedValue({ + artifactId: 'artifact-default', + fileId: 'file-default', + name: 'default.txt', + destinations: ['run'], + }); + + const mockArtifacts: IArtifactService = { + upload: uploadMock, + download: vi.fn(), + }; + + const context = createExecutionContext({ + runId: 'run-default-test', + componentRef: 'artifact-writer-default', + artifacts: mockArtifacts, + }); + + const executePayload = { + inputs: { + // artifactName not provided, should use default template + content: 'Default name test', + }, + params: { + fileExtension: '.txt', + saveToRunArtifacts: true, + publishToArtifactLibrary: false, + }, + }; + + const result = await component.execute(executePayload, context); + + expect(uploadMock).toHaveBeenCalledTimes(1); + const payload = uploadMock.mock.calls[0][0]; + // Should contain run_id and timestamp pattern + expect(payload.name).toMatch(/^run-default-test-\d+\.txt$/); + expect(result.artifactName).toMatch(/^run-default-test-\d+$/); + expect(result.saved).toBe(true); + }); }); diff --git a/worker/src/components/core/artifact-writer.ts b/worker/src/components/core/artifact-writer.ts index 292741e2..698ea9a0 100644 --- a/worker/src/components/core/artifact-writer.ts +++ b/worker/src/components/core/artifact-writer.ts @@ -11,6 +11,20 @@ import { } from '@shipsec/component-sdk'; const inputSchema = inputs({ + artifactName: port( + z + .string() + .optional() + .describe( + 'Name for the artifact. Supports dynamic placeholders: {{run_id}}, {{timestamp}}, {{dataset}}, {{task}}, {{run_name}}. Defaults to {{run_id}}-{{timestamp}}.', + ), + { + label: 'Artifact Name', + description: + 'Name for the artifact file. Use dynamic placeholders like {{run_id}}, {{timestamp}} for unique names.', + editor: 'text', + }, + ), content: port( z .any() @@ -29,17 +43,50 @@ const inputSchema = inputs({ ), }); +/** + * Substitutes dynamic placeholders in artifact name template. + * Supported placeholders: + * - {{run_id}} - Current run ID + * - {{timestamp}} - Unix timestamp in milliseconds + * - {{dataset}} - Dataset name (if available) + * - {{task}} - Task name (if available) + * - {{run_name}} - Human-readable run name + * - {{date}} - ISO date (YYYY-MM-DD) + * - {{time}} - ISO time (HH-MM-SS) + */ +function substituteArtifactName( + template: string, + context: { runId: string; componentRef: string }, +): string { + const now = new Date(); + const timestamp = now.getTime().toString(); + const isoDate = now.toISOString().split('T')[0]; // YYYY-MM-DD + const isoTime = now.toISOString().split('T')[1].split('.')[0].replace(/:/g, '-'); // HH-MM-SS + + // Extract a shorter run name from runId (last segment after last hyphen, or first 8 chars) + const runIdParts = context.runId.split('-'); + const runName = runIdParts.length > 1 ? runIdParts.slice(-1)[0] : context.runId.slice(0, 8); + + return template + .replace(/\{\{run_id\}\}/gi, context.runId) + .replace(/\{\{timestamp\}\}/gi, timestamp) + .replace(/\{\{dataset\}\}/gi, 'default') + .replace(/\{\{task\}\}/gi, context.componentRef) + .replace(/\{\{run_name\}\}/gi, runName) + .replace(/\{\{date\}\}/gi, isoDate) + .replace(/\{\{time\}\}/gi, isoTime); +} + const parameterSchema = parameters({ - fileName: param( + fileExtension: param( z .string() - .min(1, 'File name is required') - .default('artifact.txt') - .describe('File name to assign to the saved artifact.'), + .default('.txt') + .describe('File extension to append to the artifact name.'), { - label: 'File Name', + label: 'File Extension', editor: 'text', - description: 'File name to use when saving the artifact.', + description: 'File extension (e.g., .txt, .json, .csv). Will be appended to the artifact name.', }, ), mimeType: param( @@ -76,9 +123,13 @@ const outputSchema = outputs({ label: 'Artifact ID', description: 'Identifier returned by the artifact service.', }), + artifactName: port(z.string(), { + label: 'Artifact Name', + description: 'Resolved name of the artifact (with placeholders substituted).', + }), fileName: port(z.string(), { label: 'File Name', - description: 'Name of the artifact file that was written.', + description: 'Full file name including extension.', }), size: port(z.number(), { label: 'Size', @@ -125,6 +176,19 @@ const definition = defineComponent({ destinations.push('library'); } + // Resolve artifact name with dynamic placeholders + const artifactNameTemplate = inputs.artifactName || '{{run_id}}-{{timestamp}}'; + const resolvedArtifactName = substituteArtifactName(artifactNameTemplate, { + runId: context.runId, + componentRef: context.componentRef, + }); + + // Build full filename with extension + const extension = params.fileExtension || '.txt'; + const fileName = resolvedArtifactName.endsWith(extension) + ? resolvedArtifactName + : `${resolvedArtifactName}${extension}`; + // Serialize content to string - if already a string, use as-is; otherwise JSON stringify const rawContent = inputs.content; let serializedContent: string; @@ -141,7 +205,8 @@ const definition = defineComponent({ context.logger.info('[ArtifactWriter] No destinations selected; skipping upload.'); return { artifactId: undefined, - fileName: params.fileName, + artifactName: resolvedArtifactName, + fileName, size: Buffer.byteLength(serializedContent), destinations: [], saved: false, @@ -157,13 +222,13 @@ const definition = defineComponent({ const buffer = Buffer.from(serializedContent, 'utf-8'); context.logger.info( - `[ArtifactWriter] Uploading '${params.fileName}' (${buffer.byteLength} bytes) to ${destinations.join( + `[ArtifactWriter] Uploading '${fileName}' (${buffer.byteLength} bytes) to ${destinations.join( ', ', )}`, ); const upload = await context.artifacts.upload({ - name: params.fileName, + name: fileName, mimeType: params.mimeType ?? 'text/plain', content: buffer, destinations, @@ -171,7 +236,8 @@ const definition = defineComponent({ return { artifactId: upload.artifactId, - fileName: params.fileName, + artifactName: resolvedArtifactName, + fileName, size: buffer.byteLength, destinations, saved: true, From cf75d6c254483bb8d6537203c9ea512cb0372b2e Mon Sep 17 00:00:00 2001 From: Krishna Mohan Date: Tue, 27 Jan 2026 17:09:33 +0530 Subject: [PATCH 2/5] fix: linting issue fixed Signed-off-by: Krishna Mohan --- frontend/src/components/workflow/ConfigPanel.tsx | 9 +++++---- .../workflow/DynamicArtifactNameInput.tsx | 16 +++++----------- worker/src/components/core/artifact-writer.ts | 8 +++----- 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/frontend/src/components/workflow/ConfigPanel.tsx b/frontend/src/components/workflow/ConfigPanel.tsx index ca6fd0f8..ae469aa3 100644 --- a/frontend/src/components/workflow/ConfigPanel.tsx +++ b/frontend/src/components/workflow/ConfigPanel.tsx @@ -955,8 +955,10 @@ export function ConfigPanel({ /> )} {/* Skip helper text for DynamicArtifactNameInput as it has its own */} - {!(component?.id === 'core.artifact.writer' && input.id === 'artifactName') && ( - manualLocked ? ( + {!( + component?.id === 'core.artifact.writer' && input.id === 'artifactName' + ) && + (manualLocked ? (

Disconnect the port to edit manual input.

@@ -968,8 +970,7 @@ export function ConfigPanel({ ? 'Add entries or clear manual input to require a port connection.' : 'Leave blank to require a port connection.'}

- ) - )} + ))} )} diff --git a/frontend/src/components/workflow/DynamicArtifactNameInput.tsx b/frontend/src/components/workflow/DynamicArtifactNameInput.tsx index d179fc64..81068740 100644 --- a/frontend/src/components/workflow/DynamicArtifactNameInput.tsx +++ b/frontend/src/components/workflow/DynamicArtifactNameInput.tsx @@ -51,9 +51,7 @@ export function DynamicArtifactNameInput({ className="text-sm font-mono" disabled={disabled} /> -

- e.g., task123-1617181920 -

+

e.g., task123-1617181920

{/* Dynamic Parameters Section */}
@@ -85,7 +83,7 @@ export function DynamicArtifactNameInput({ className={cn( 'w-full flex items-center gap-2 px-2 py-1.5 rounded-md text-left transition-colors', 'hover:bg-primary/10 group', - disabled && 'opacity-50 cursor-not-allowed' + disabled && 'opacity-50 cursor-not-allowed', )} >
- - {param.placeholder} - + {param.placeholder}
- - {param.description} - + {param.description} ))}
diff --git a/worker/src/components/core/artifact-writer.ts b/worker/src/components/core/artifact-writer.ts index 698ea9a0..9f2db346 100644 --- a/worker/src/components/core/artifact-writer.ts +++ b/worker/src/components/core/artifact-writer.ts @@ -79,14 +79,12 @@ function substituteArtifactName( const parameterSchema = parameters({ fileExtension: param( - z - .string() - .default('.txt') - .describe('File extension to append to the artifact name.'), + z.string().default('.txt').describe('File extension to append to the artifact name.'), { label: 'File Extension', editor: 'text', - description: 'File extension (e.g., .txt, .json, .csv). Will be appended to the artifact name.', + description: + 'File extension (e.g., .txt, .json, .csv). Will be appended to the artifact name.', }, ), mimeType: param( From 1e1455231c1a0d3eeb44e06544cf42cd5fd21e2f Mon Sep 17 00:00:00 2001 From: Krishna Mohan Date: Fri, 30 Jan 2026 14:16:20 +0530 Subject: [PATCH 3/5] fix: fixed the placeholder naming option match Signed-off-by: Krishna Mohan --- .../workflow/DynamicArtifactNameInput.tsx | 100 +++++++----------- worker/src/components/core/artifact-writer.ts | 16 +-- 2 files changed, 41 insertions(+), 75 deletions(-) diff --git a/frontend/src/components/workflow/DynamicArtifactNameInput.tsx b/frontend/src/components/workflow/DynamicArtifactNameInput.tsx index 81068740..14193a65 100644 --- a/frontend/src/components/workflow/DynamicArtifactNameInput.tsx +++ b/frontend/src/components/workflow/DynamicArtifactNameInput.tsx @@ -1,7 +1,11 @@ -import { useState } from 'react'; -import { ChevronDown, ChevronRight, Plus } from 'lucide-react'; import { Input } from '@/components/ui/input'; -import { cn } from '@/lib/utils'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; interface DynamicParameter { placeholder: string; @@ -9,11 +13,9 @@ interface DynamicParameter { } const DYNAMIC_PARAMETERS: DynamicParameter[] = [ - { placeholder: '{{timestamp}}', description: 'Current timestamp' }, - { placeholder: '{{run_id}}', description: 'Run ID' }, - { placeholder: '{{dataset}}', description: 'Dataset name' }, - { placeholder: '{{task}}', description: 'Task name' }, - { placeholder: '{{run_name}}', description: 'Run name' }, + { placeholder: '{{run_id}}', description: 'Full workflow run ID' }, + { placeholder: '{{node_id}}', description: 'Component node ID in workflow' }, + { placeholder: '{{timestamp}}', description: 'Unix timestamp (ms)' }, { placeholder: '{{date}}', description: 'Date (YYYY-MM-DD)' }, { placeholder: '{{time}}', description: 'Time (HH-MM-SS)' }, ]; @@ -31,13 +33,12 @@ export function DynamicArtifactNameInput({ disabled = false, placeholder = '{{run_id}}-{{timestamp}}', }: DynamicArtifactNameInputProps) { - const [isParamsOpen, setIsParamsOpen] = useState(true); const currentValue = value || ''; - const handleInsertPlaceholder = (placeholder: string) => { + const handleInsertPlaceholder = (selectedPlaceholder: string) => { if (disabled) return; // Insert at the end of current value - const newValue = currentValue ? `${currentValue}${placeholder}` : placeholder; + const newValue = currentValue ? `${currentValue}${selectedPlaceholder}` : selectedPlaceholder; onChange(newValue); }; @@ -51,61 +52,34 @@ export function DynamicArtifactNameInput({ className="text-sm font-mono" disabled={disabled} /> -

e.g., task123-1617181920

- {/* Dynamic Parameters Section */} -
-
); } diff --git a/worker/src/components/core/artifact-writer.ts b/worker/src/components/core/artifact-writer.ts index 9f2db346..a31d51fe 100644 --- a/worker/src/components/core/artifact-writer.ts +++ b/worker/src/components/core/artifact-writer.ts @@ -16,7 +16,7 @@ const inputSchema = inputs({ .string() .optional() .describe( - 'Name for the artifact. Supports dynamic placeholders: {{run_id}}, {{timestamp}}, {{dataset}}, {{task}}, {{run_name}}. Defaults to {{run_id}}-{{timestamp}}.', + 'Name for the artifact. Supports dynamic placeholders: {{run_id}}, {{node_id}}, {{timestamp}}, {{date}}, {{time}}. Defaults to {{run_id}}-{{timestamp}}.', ), { label: 'Artifact Name', @@ -46,11 +46,9 @@ const inputSchema = inputs({ /** * Substitutes dynamic placeholders in artifact name template. * Supported placeholders: - * - {{run_id}} - Current run ID + * - {{run_id}} - Full workflow run ID + * - {{node_id}} - Component's node ID in the workflow (e.g., "artifact-writer-1") * - {{timestamp}} - Unix timestamp in milliseconds - * - {{dataset}} - Dataset name (if available) - * - {{task}} - Task name (if available) - * - {{run_name}} - Human-readable run name * - {{date}} - ISO date (YYYY-MM-DD) * - {{time}} - ISO time (HH-MM-SS) */ @@ -63,16 +61,10 @@ function substituteArtifactName( const isoDate = now.toISOString().split('T')[0]; // YYYY-MM-DD const isoTime = now.toISOString().split('T')[1].split('.')[0].replace(/:/g, '-'); // HH-MM-SS - // Extract a shorter run name from runId (last segment after last hyphen, or first 8 chars) - const runIdParts = context.runId.split('-'); - const runName = runIdParts.length > 1 ? runIdParts.slice(-1)[0] : context.runId.slice(0, 8); - return template .replace(/\{\{run_id\}\}/gi, context.runId) + .replace(/\{\{node_id\}\}/gi, context.componentRef) .replace(/\{\{timestamp\}\}/gi, timestamp) - .replace(/\{\{dataset\}\}/gi, 'default') - .replace(/\{\{task\}\}/gi, context.componentRef) - .replace(/\{\{run_name\}\}/gi, runName) .replace(/\{\{date\}\}/gi, isoDate) .replace(/\{\{time\}\}/gi, isoTime); } From df3e16c9f8751c230dc09c2ca536272f1d224373 Mon Sep 17 00:00:00 2001 From: Krishna Mohan Date: Fri, 30 Jan 2026 14:18:33 +0530 Subject: [PATCH 4/5] fix: fixed linting issue Signed-off-by: Krishna Mohan --- .../components/workflow/DynamicArtifactNameInput.tsx | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/frontend/src/components/workflow/DynamicArtifactNameInput.tsx b/frontend/src/components/workflow/DynamicArtifactNameInput.tsx index 14193a65..0f838ba0 100644 --- a/frontend/src/components/workflow/DynamicArtifactNameInput.tsx +++ b/frontend/src/components/workflow/DynamicArtifactNameInput.tsx @@ -54,21 +54,13 @@ export function DynamicArtifactNameInput({ />
- {DYNAMIC_PARAMETERS.map((param) => ( - + {param.placeholder} — {param.description} From 867151c6deb2976ed8365beda0f0a1695016e491 Mon Sep 17 00:00:00 2001 From: Krishna Mohan Date: Fri, 30 Jan 2026 14:23:18 +0530 Subject: [PATCH 5/5] fix: changed the test case option for artifact naming Signed-off-by: Krishna Mohan --- worker/src/components/core/__tests__/artifact-writer.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worker/src/components/core/__tests__/artifact-writer.test.ts b/worker/src/components/core/__tests__/artifact-writer.test.ts index c8740d58..d70e39b8 100644 --- a/worker/src/components/core/__tests__/artifact-writer.test.ts +++ b/worker/src/components/core/__tests__/artifact-writer.test.ts @@ -94,7 +94,7 @@ describe('core.artifact.writer component', () => { const executePayload = { inputs: { - artifactName: '{{run_id}}-{{task}}', + artifactName: '{{run_id}}-{{node_id}}', content: { data: 'test' }, }, params: {