diff --git a/bespin/commands.py b/bespin/commands.py
index e30d252..1766e96 100644
--- a/bespin/commands.py
+++ b/bespin/commands.py
@@ -3,6 +3,7 @@ from bespin.config import ConfigFile
 from bespin.api import BespinApi
 from bespin.exceptions import IncompleteJobFileException
 from bespin.dukeds import DDSFileUtil
+from bespin.dukeds import PATH_PREFIX as DUKEDS_PATH_PREFIX
 from tabulate import tabulate
 import yaml
 import json
@@ -411,8 +412,8 @@ class JobOrderWalker(object):
         :param path: str: format dds://<projectname>/<filepath>
         :return: str: file path to be used for staging data when running the workflow
         """
-        if path.startswith("dds://"):
-            return path.replace("dds://", "dds_").replace("/", "_").replace(":", "_")
+        if path.startswith(DUKEDS_PATH_PREFIX):
+            return path.replace(DUKEDS_PATH_PREFIX, "dds_").replace("/", "_").replace(":", "_")
         return path
 
 
@@ -421,8 +422,10 @@ class JobOrderPlaceholderCheck(JobOrderWalker):
         self.keys_with_placeholders = set()
 
     def on_class_value(self, top_level_key, value):
-        if value['class'] == 'File' and value['path'] in USER_PLACEHOLDER_VALUES:
-            self.keys_with_placeholders.add(top_level_key)
+        if value['class'] == 'File':
+            path = value.get('path')
+            if path and path in USER_PLACEHOLDER_VALUES:
+                self.keys_with_placeholders.add(top_level_key)
 
     def on_simple_value(self, top_level_key, value):
         if value in USER_PLACEHOLDER_VALUES:
@@ -432,7 +435,9 @@ class JobOrderPlaceholderCheck(JobOrderWalker):
 class JobOrderFormatFiles(JobOrderWalker):
     def on_class_value(self, top_level_key, value):
         if value['class'] == 'File':
-            value['path'] = self.format_file_path(value['path'])
+            path = value.get('path')
+            if path:
+                value['path'] = self.format_file_path(path)
 
 
 class JobOrderFileDetails(JobOrderWalker):
@@ -442,6 +447,7 @@ class JobOrderFileDetails(JobOrderWalker):
 
     def on_class_value(self, top_level_key, value):
         if value['class'] == 'File':
-            path = value['path']
-            dds_file = self.dds_file_util.find_file_for_path(path)
-            self.dds_files.append((dds_file, self.format_file_path(path)))
+            path = value.get('path')
+            if path and path.startswith(DUKEDS_PATH_PREFIX):
+                dds_file = self.dds_file_util.find_file_for_path(path)
+                self.dds_files.append((dds_file, self.format_file_path(path)))

Allow using non-DukeDS File parameters
Currently all files are expected to begin with the `dds:` prefix. If a user attempts to use a ` class: File` input specifying either a url or a file path they will receive an error.
```
Invalid DukeDS file path (missing prefix): /data/exome-seq/b37/SUPERSNIPS.vcf
```
If a user attempts to use the location option they receive the following:
```
...
    self.on_class_value(top_level_key, obj)
  File "/Users/jpb67/Documents/work/bespin-cli/env3/lib/python3.6/site-packages/bespin/commands.py", line 424, in on_class_value
    if value['class'] == 'File' and value['path'] in USER_PLACEHOLDER_VALUES:
KeyError: 'path'
```

Documentation on CWL File:
https://www.commonwl.org/v1.0/Workflow.html#File


diff --git a/bespin/test_commands.py b/bespin/test_commands.py
index 486bf81..c03c245 100644
--- a/bespin/test_commands.py
+++ b/bespin/test_commands.py
@@ -250,6 +250,14 @@ class JobFileTestCase(TestCase):
                 'class': 'File',
                 'path': 'dds://project/somepath.txt'
             },
+            'my_path_file': {
+                'class': 'File',
+                'path': '/tmp/data.txt'
+            },
+            'my_url_file': {
+                'class': 'File',
+                'location': 'https://github.com/datafile1.dat'
+            },
             'myint': 123,
             'myfileary': [
                 {
@@ -298,6 +306,14 @@ class JobFileTestCase(TestCase):
                  'path': 'dds_myproject_rawData_SAAAA_R2_001.fastq.gz'
              },
              'name': 'Sample1'}])
+        self.assertEqual(user_job_order['my_path_file'], {
+            'class': 'File',
+            'path': '/tmp/data.txt'
+        }, "Plain file paths should not be modified.")
+        self.assertEqual(user_job_order['my_url_file'], {
+                'class': 'File',
+                'location': 'https://github.com/datafile1.dat'
+        }, "URL file paths should not be modified.")
 
     @patch('bespin.commands.DDSFileUtil')
     def test_get_dds_files_details(self, mock_dds_file_util):
@@ -529,7 +545,15 @@ class JobOrderWalkerTestCase(TestCase):
                     'class': 'File',
                     'path': 'somepath3'
                 }]
-            }
+            },
+            'plain_path_file': {
+                'class': 'File',
+                'path': '/tmp/data.txt'
+            },
+            'url_file': {
+                'class': 'File',
+                'location': 'https://github.com/datafile1.dat'
+            },
         })
 
         walker.on_simple_value.assert_has_calls([
@@ -592,6 +616,14 @@ class JobOrderPlaceholderCheckTestCase(TestCase):
                     'path': FILE_PLACEHOLDER,
                 }
             },
+            'plain_path_file': {
+                'class': 'File',
+                'path': '/tmp/data.txt'
+            },
+            'url_file': {
+                'class': 'File',
+                'location': 'https://github.com/datafile1.dat'
+            },
         }
         expected_keys = [
             'bad_str', 'bad_int', 'bad_file', 'bad_str_ary', 'bad_file_ary', 'bad_file_dict',
@@ -623,6 +655,14 @@ class JobOrderFormatFilesTestCase(TestCase):
                     'path': 'dds://project3/data/other/somepath.txt',
                 }
             },
+            'plain_path_file': {
+                'class': 'File',
+                'path': '/tmp/data.txt'
+            },
+            'url_file': {
+                'class': 'File',
+                'location': 'https://github.com/datafile1.dat'
+            },
         }
 
         formatter = JobOrderFormatFiles()
@@ -661,6 +701,14 @@ class JobOrderFileDetailsTestCase(TestCase):
                     'path': 'dds://project3/data/other/somepath.txt',
                 }
             },
+            'plain_path_file': {
+                'class': 'File',
+                'path': '/tmp/data.txt'
+            },
+            'url_file': {
+                'class': 'File',
+                'location': 'https://github.com/datafile1.dat'
+            },
         }
         expected_dds_file_info = [
             ('ddsfiledata', 'dds_project1_data_somepath.txt'),

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest mock
apt-get update apt-get install -y gcc
3.9
requirements/base.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
azure-common==1.1.28
azure-core==1.32.0
azure-identity==1.21.0
azure-mgmt-core==1.5.0
azure-mgmt-storage==22.1.1
azure-storage-blob==12.25.1
azure-storage-file-datalake==12.20.0
-e git+https://github.com/Duke-GCB/bespin-cli.git@3cb0cf75ca821e051ea48d4b1a990f34acedd98d#egg=bespin_cli
certifi==2025.1.31
cffi==1.17.1
charset-normalizer==3.4.1
cryptography==44.0.2
DukeDSClient==4.0.0
exceptiongroup==1.2.2
future==1.0.0
idna==3.10
iniconfig==2.1.0
isodate==0.7.2
mock==5.2.0
msal==1.32.0
msal-extensions==1.3.1
msgraph-core==0.2.2
packaging==24.2
pluggy==1.5.0
pycparser==2.22
PyJWT==2.10.1
pytest==8.3.5
pytz==2025.2
PyYAML==6.0.2
requests==2.32.3
six==1.17.0
tabulate==0.9.0
tenacity==6.2.0
tomli==2.2.1
typing_extensions==4.13.0
urllib3==2.3.0

name: bespin-cli
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - azure-common==1.1.28
      - azure-core==1.32.0
      - azure-identity==1.21.0
      - azure-mgmt-core==1.5.0
      - azure-mgmt-storage==22.1.1
      - azure-storage-blob==12.25.1
      - azure-storage-file-datalake==12.20.0
      - certifi==2025.1.31
      - cffi==1.17.1
      - charset-normalizer==3.4.1
      - cryptography==44.0.2
      - dukedsclient==4.0.0
      - exceptiongroup==1.2.2
      - future==1.0.0
      - idna==3.10
      - iniconfig==2.1.0
      - isodate==0.7.2
      - mock==5.2.0
      - msal==1.32.0
      - msal-extensions==1.3.1
      - msgraph-core==0.2.2
      - packaging==24.2
      - pluggy==1.5.0
      - pycparser==2.22
      - pyjwt==2.10.1
      - pytest==8.3.5
      - pytz==2025.2
      - pyyaml==6.0.2
      - requests==2.32.3
      - six==1.17.0
      - tabulate==0.9.0
      - tenacity==6.2.0
      - tomli==2.2.1
      - typing-extensions==4.13.0
      - urllib3==2.3.0
prefix: /opt/conda/envs/bespin-cli

bespin/test_commands.py::JobFileTestCase::test_create_user_job_order_json bespin/test_commands.py::JobOrderPlaceholderCheckTestCase::test_walk bespin/test_commands.py::JobOrderFormatFilesTestCase::test_walk bespin/test_commands.py::JobOrderFileDetailsTestCase::test_walk
bespin/test_commands.py::JobFileTestCase::test_yaml_str
bespin/test_commands.py::CommandsTestCase::test_cancel_job bespin/test_commands.py::CommandsTestCase::test_create_job bespin/test_commands.py::CommandsTestCase::test_delete_job bespin/test_commands.py::CommandsTestCase::test_init_job bespin/test_commands.py::CommandsTestCase::test_jobs_list bespin/test_commands.py::CommandsTestCase::test_restart_job bespin/test_commands.py::CommandsTestCase::test_start_job_no_token bespin/test_commands.py::CommandsTestCase::test_start_job_with_token bespin/test_commands.py::CommandsTestCase::test_workflows_list_all_versions bespin/test_commands.py::CommandsTestCase::test_workflows_list_latest_versions bespin/test_commands.py::TableTestCase::test_str bespin/test_commands.py::WorkflowDetailsTestCase::test_get_column_data bespin/test_commands.py::WorkflowDetailsTestCase::test_ignores_workflows_without_versions_when_all bespin/test_commands.py::WorkflowDetailsTestCase::test_ignores_workflows_without_versions_when_latest bespin/test_commands.py::JobFileTestCase::test_create_job bespin/test_commands.py::JobFileTestCase::test_get_dds_files_details bespin/test_commands.py::JobFileLoaderTestCase::test_create_job_file bespin/test_commands.py::JobFileLoaderTestCase::test_validate_job_file_data_invalid_job_order_params bespin/test_commands.py::JobFileLoaderTestCase::test_validate_job_file_data_invalid_name_and_fund_code bespin/test_commands.py::JobFileLoaderTestCase::test_validate_job_file_data_ok bespin/test_commands.py::JobQuestionnaireTestCase::test_create_job_file_with_placeholders bespin/test_commands.py::JobQuestionnaireTestCase::test_create_placeholder_value bespin/test_commands.py::JobQuestionnaireTestCase::test_format_user_fields bespin/test_commands.py::JobOrderWalkerTestCase::test_format_file_path bespin/test_commands.py::JobOrderWalkerTestCase::test_walk bespin/test_commands.py::JobsListTestCase::test_column_names bespin/test_commands.py::JobsListTestCase::test_get_column_data bespin/test_commands.py::JobsListTestCase::test_get_elapsed_hours bespin/test_commands.py::JobsListTestCase::test_get_workflow_tag
bespin/commands.py
bespin/commands.py